第一步读文档。记笔记。

State

Props 的只读性

数据的单向传递,任何情况下都不能在子组件中修改props。⭐

正确地使用 State

  1. 不要直接修改 State ```javascript // Wrong this.state.comment = ‘Hello’;

// Correct this.setState({comment: ‘Hello’});

  1. 2. State 的更新可能是异步的
  2. > 出于性能考虑,React 可能会把多个 setState() 调用合并成一个调用。因为 this.props this.state 可能会异步更新,所以你不要依赖他们的值来更新下一个状态。
  3. ```javascript
  4. // Wrong
  5. this.setState({
  6. counter: this.state.counter + this.props.increment,
  7. });
  8. // Correct
  9. this.setState((state, props) => ({
  10. counter: state.counter + props.increment
  11. }));
  1. State 的更新会被合并

    事件处理

  • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
  • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
    1. <button onClick={activateLasers}>
    2. Activate Lasers
    3. </button>

    你必须谨慎对待 JSX 回调函数中的 this,在 JavaScript 中,class 的方法默认不会绑定 this。如果你忘记绑定 this.handleClick 并把它传入了 onClick,当你调用这个函数的时候 this 的值为 undefined。

有三种方法可以绑定this

  1. 箭头函数

    1. class LoggingButton extends React.Component {
    2. // 此语法确保 `handleClick` 内的 `this` 已被绑定。
    3. // 注意: 这是 *实验性* 语法。
    4. handleClick = () => {
    5. console.log('this is:', this);
    6. }
    7. render() {
    8. return (
    9. <button onClick={this.handleClick}>
    10. Click me
    11. </button>
    12. );
    13. }
    14. }
  2. 回调中使用箭头函数

    1. class LoggingButton extends React.Component {
    2. handleClick() {
    3. console.log('this is:', this);
    4. }
    5. render() {
    6. // 此语法确保 `handleClick` 内的 `this` 已被绑定。
    7. return (
    8. <button onClick={() => this.handleClick()}>
    9. Click me
    10. </button>
    11. );
    12. }
    13. }
  3. 显式绑定

    1. class Toggle extends React.Component {
    2. constructor(props) {
    3. super(props);
    4. this.state = {isToggleOn: true};
    5. // 为了在回调中使用 `this`,这个绑定是必不可少的
    6. this.handleClick = this.handleClick.bind(this);
    7. }
    8. handleClick() {
    9. this.setState(prevState => ({
    10. isToggleOn: !prevState.isToggleOn
    11. }));
    12. }
    13. render() {
    14. return (
    15. <button onClick={this.handleClick}>
    16. {this.state.isToggleOn ? 'ON' : 'OFF'}
    17. </button>
    18. );
    19. }
    20. }

    向事件处理程序传递参数

    在循环中,通常我们会为事件处理函数传递额外的参数。

    1. <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>
    2. <button onClick={this.deleteRow.bind(this, id)}>Delete Row</button>

    key

    组合 vs 继承

    React中的slot

    props.children ```jsx function FancyBorder(props) { return (

    {props.children}
    ); }

function WelcomeDialog() { return (

Welcome

Thank you for visiting our spacecraft!

); }

  1. <a name="GdHyy"></a>
  2. ### 具名插槽
  3. 通过props传递即可,在react中万物皆可传。
  4. ```jsx
  5. function SplitPane(props) {
  6. return (
  7. <div className="SplitPane">
  8. <div className="SplitPane-left">
  9. {props.left}
  10. </div>
  11. <div className="SplitPane-right">
  12. {props.right}
  13. </div>
  14. </div>
  15. );
  16. }
  17. function App() {
  18. return (
  19. <SplitPane
  20. left={
  21. <Contacts />
  22. }
  23. right={
  24. <Chat />
  25. } />
  26. );
  27. }

高级指引

Fragment

类似vue中的template,但是react中可以设置为<></>.

React.lazy

与vue3中的类似,结合Suspense组件可以实现异步加载组件。

  1. import React, { Suspense } from 'react';
  2. const OtherComponent = React.lazy(() => import('./OtherComponent'));
  3. const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
  4. function MyComponent() {
  5. return (
  6. <div>
  7. <Suspense fallback={<div>Loading...</div>}>
  8. <section>
  9. <OtherComponent />
  10. <AnotherComponent />
  11. </section>
  12. </Suspense>
  13. </div>
  14. );
  15. }

异常捕获边界(Error boundaries)

  1. import React, { Suspense } from 'react';
  2. import MyErrorBoundary from './MyErrorBoundary';
  3. const OtherComponent = React.lazy(() => import('./OtherComponent'));
  4. const AnotherComponent = React.lazy(() => import('./AnotherComponent'));
  5. const MyComponent = () => (
  6. <div>
  7. <MyErrorBoundary>
  8. <Suspense fallback={<div>Loading...</div>}>
  9. <section>
  10. <OtherComponent />
  11. <AnotherComponent />
  12. </section>
  13. </Suspense>
  14. </MyErrorBoundary>
  15. </div>
  16. );

基于路由的代码分割

  1. import React, { Suspense, lazy } from 'react';
  2. import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
  3. const Home = lazy(() => import('./routes/Home'));
  4. const About = lazy(() => import('./routes/About'));
  5. const App = () => (
  6. <Router>
  7. <Suspense fallback={<div>Loading...</div>}>
  8. <Switch>
  9. <Route exact path="/" component={Home}/>
  10. <Route path="/about" component={About}/>
  11. </Switch>
  12. </Suspense>
  13. </Router>
  14. );

Context⭐

类似于vue的provide
主要API:

  • React.createContext() =>第一参数是默认值
  • Context.Provider
    • <MyContext.Provider value={/* 某个值 */}>
  • Context.Consumer

    1. <ThemeContext.Consumer>
    2. {({theme, toggleTheme}) => (
    3. <button
    4. onClick={toggleTheme}
    5. style={{backgroundColor: theme.background}}>
    6. Toggle Theme
    7. </button>
    8. )}
    9. </ThemeContext.Consumer>
  • Context.displayName

context 对象接受一个名为 displayName 的 property,类型为字符串。React DevTools 使用该字符串来确定 context 要显示的内容。
示例,下述组件在 DevTools 中将显示为 MyDisplayName:

  1. const MyContext = React.createContext(/* some value */);
  2. MyContext.displayName = 'MyDisplayName';
  3. <MyContext.Provider> // "MyDisplayName.Provider" 在 DevTools 中
  4. <MyContext.Consumer> // "MyDisplayName.Consumer" 在 DevTools 中
  1. // Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
  2. // 为当前的 theme 创建一个 context(“light”为默认值)。
  3. const ThemeContext = React.createContext('light');
  4. class App extends React.Component {
  5. render() {
  6. // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
  7. // 无论多深,任何组件都能读取这个值。
  8. // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
  9. return (
  10. <ThemeContext.Provider value="dark">
  11. <Toolbar />
  12. </ThemeContext.Provider>
  13. );
  14. }
  15. }
  16. // 中间的组件再也不必指明往下传递 theme 了。
  17. function Toolbar() {
  18. return (
  19. <div>
  20. <ThemedButton />
  21. </div>
  22. );
  23. }
  24. class ThemedButton extends React.Component {
  25. // 指定 contextType 读取当前的 theme context。
  26. // React 会往上找到最近的 theme Provider,然后使用它的值。
  27. // 在这个例子中,当前的 theme 值为 “dark”。
  28. static contextType = ThemeContext;
  29. render() {
  30. return <Button theme={this.context} />;
  31. }
  32. }

Context 主要应用场景在于很多不同层级的组件需要访问同样一些的数据。请谨慎使用,因为这会使得组件的复用性变差。

如果你只是想避免层层传递一些属性,组件组合(component composition)有时候是一个比 context 更好的解决方案。

官方例子

错误边界

如果一个 class 组件中定义了 static getDerivedStateFromError() 或 componentDidCatch() 这两个生命周期方法中的任意一个(或两个)时,那么它就变成一个错误边界。当抛出错误后,请使用 static getDerivedStateFromError() 渲染备用 UI ,使用 componentDidCatch() 打印错误信息。

  1. class ErrorBoundary extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = { hasError: false };
  5. }
  6. static getDerivedStateFromError(error) {
  7. // 更新 state 使下一次渲染能够显示降级后的 UI
  8. return { hasError: true };
  9. }
  10. componentDidCatch(error, errorInfo) {
  11. // 你同样可以将错误日志上报给服务器
  12. logErrorToMyService(error, errorInfo);
  13. }
  14. render() {
  15. if (this.state.hasError) {
  16. // 你可以自定义降级后的 UI 并渲染
  17. return <h1>Something went wrong.</h1>;
  18. }
  19. return this.props.children;
  20. }
  21. }
  1. <ErrorBoundary>
  2. <MyWidget />
  3. </ErrorBoundary>

Refs 转发

没看懂 , 需要加深理解,链接

shouldComponentUpdate

  1. class CounterButton extends React.Component {
  2. constructor(props) {
  3. super(props);
  4. this.state = {count: 1};
  5. }
  6. shouldComponentUpdate(nextProps, nextState) {
  7. if (this.props.color !== nextProps.color) {
  8. return true;
  9. }
  10. if (this.state.count !== nextState.count) {
  11. return true;
  12. }
  13. return false;
  14. }
  15. render() {
  16. return (
  17. <button
  18. color={this.props.color}
  19. onClick={() => this.setState(state => ({count: state.count + 1}))}>
  20. Count: {this.state.count}
  21. </button>
  22. );
  23. }
  24. }

在这段代码中,shouldComponentUpdate 仅检查了 props.color 或 state.count 是否改变。如果这些值没有改变,那么这个组件不会更新。如果你的组件更复杂一些,你可以使用类似“浅比较”的模式来检查 props 和 state 中所有的字段,以此来决定是否组件需要更新。React 已经提供了一位好帮手来帮你实现这种常见的模式 - 你只要继承 React.PureComponent 就行了。所以这段代码可以改成以下这种更简洁的形式:

  1. class CounterButton extends React.PureComponent {
  2. constructor(props) {
  3. super(props);
  4. this.state = {count: 1};
  5. }
  6. render() {
  7. return (
  8. <button
  9. color={this.props.color}
  10. onClick={() => this.setState(state => ({count: state.count + 1}))}>
  11. Count: {this.state.count}
  12. </button>
  13. );
  14. }
  15. }

Portals

类似于vue3的传送门Teleport

  1. render() {
  2. // React 并*没有*创建一个新的 div。它只是把子元素渲染到 `domNode` 中。
  3. // `domNode` 是一个可以在任何位置的有效 DOM 节点。
  4. return ReactDOM.createPortal(
  5. this.props.children,
  6. domNode
  7. );
  8. }

Profiler API

  1. render(
  2. <App>
  3. <Profiler id="Navigation" onRender={callback}>
  4. <Navigation {...props} />
  5. </Profiler>
  6. <Main {...props} />
  7. </App>
  8. );

onRender

  1. function onRenderCallback(
  2. id, // 发生提交的 Profiler 树的 “id”
  3. phase, // "mount" (如果组件树刚加载) 或者 "update" (如果它重渲染了)之一
  4. actualDuration, // 本次更新 committed 花费的渲染时间
  5. baseDuration, // 估计不使用 memoization 的情况下渲染整颗子树需要的时间
  6. startTime, // 本次更新中 React 开始渲染的时间
  7. commitTime, // 本次更新中 React committed 的时间
  8. interactions // 属于本次更新的 interactions 的集合
  9. ) {
  10. // 合计或记录渲染时间。。。
  11. }

各参数详情