Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save hacker0limbo/f62f2bd6b9e6b158f8c345e63341d569 to your computer and use it in GitHub Desktop.

Select an option

Save hacker0limbo/f62f2bd6b9e6b158f8c345e63341d569 to your computer and use it in GitHub Desktop.

Revisions

  1. hacker0limbo created this gist Sep 25, 2019.
    93 changes: 93 additions & 0 deletions hook 和 class 中 state 与 props 差异.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,93 @@
    两个计数器代码:

    使用 class:
    ```javascript
    import React, {Component} from "react";
    import ReactDOM from "react-dom";

    class Example extends Component {
    state = {
    count: 0
    };
    componentDidMount() {
    setTimeout(() => {
    console.log(`You clicked ${this.state.count} times`);
    }, 3000);
    }
    componentDidUpdate() {
    setTimeout(() => {
    console.log(`You clicked ${this.state.count} times`);
    }, 3000);
    }
    render() {
    return (
    <div>
    <p>You clicked {this.state.count} times</p>
    <button onClick={() => this.setState({
    count: this.state.count + 1
    })}>
    Click me
    </button>
    </div>
    )
    }
    }

    const rootElement = document.getElementById("root");
    ReactDOM.render(<Example />, rootElement);
    ```

    使用 hook:
    ```javascript
    import React, {useState, useEffect} from "react";
    import ReactDOM from "react-dom";

    function Example() {
    const [count, setCount] = useState(0);

    useEffect(() => {
    setTimeout(() => {
    console.log(`You clicked ${count} times`);
    }, 3000);
    });

    return (
    <div>
    <p>You clicked {count} times</p>
    <button onClick={() => setCount(prevCount => prevCount + 1)}>
    Click me
    </button>
    </div>
    );
    }

    const rootElement = document.getElementById("root");
    ReactDOM.render(<Example />, rootElement);
    ```

    操作: 连续点击 5 次增加按钮:
    - hook 的组件 timeout 以后依次显示 1, 2, 3, 4, 5
    - class 的组件 timeout 以后依次显示 5, 5, 5, 5, 5

    ### 原因
    由于函数组件使用闭包的特点, 每一次调用`useEffect()`函数, 里面的 count 都是当前的 state, 相当于渲染 5 次, 每次都是一份快照, 里面的 state 也是独立的, 因此最后的结果也是符合直觉

    class 组件在每次 state 更新重新渲染以后, 都会去修改 `this.props`或者`this.state`, 这样永远都是拿到的最新的 state 或者 props

    对于函数式组件, 如果想要获取 state 的最新值, 需要使用 ref

    同时, 函数式组件里面, 例如使用`setCount(count+1)`这种, 这里的`count`永远都是一个****, 因此尽量使用`setCount(prevCount => prevCount+1)`这种写法进行 state 更新, 如下的例子设置 state 永远只会设置一次, 例如:
    ```javascript
    setCount(count+1)
    setCount(count+1)
    ```
    由于闭包的原因, 这里相当于:
    ```javascript
    setCount(2)
    setCount(2)
    ```

    **关于`useEffect`**:
    最后, 如果一个函数有使用到 props 或者 state, 那么这个函数会参与到数据流中, 两种办法:
    - 使用`useCallback`包裹, 当然其中的 dependency array 里需要包含该函数访问到的组件里的 props 和 state
    - 直接将该函数放入`useEffect`中, dependency array 里面同样需要包含该函数访问到的 props 和 state