一直以来对于函数式组件执行顺序都有一点小疑惑,在这里小记一下。废话不多说,直接上代码:

    1. function Test(props) {
    2. const [count, setCount] = useState(0)
    3. useEffect(() => {
    4. console.log('2222')
    5. })
    6. console.log('1111')
    7. return (
    8. <>
    9. {count}
    10. {console.log('3333')} {/*不规范*/}
    11. <button onClick={() => setCount(count + 1)}>+1</button>
    12. </>
    13. )
    14. }
    15. /*
    16. 执行顺序:
    17. 1111
    18. 3333
    19. 2222
    20. */

    初次渲染组件和点击按钮使得组件重新刷新,打印结果都如上所示。可得结论,写在函数式组件体的console.log最先执行,其次是渲染时的console.log,最后由于useEffect是在浏览器渲染完试图异步执行,因此useEffect中的console.log最后执行。

    利用这一特性,我们可以在函数式组件中获取上一轮的 props 或 state。

    1. const [count, setCount] = useState(0);
    2. const prevCountRef = useRef();
    3. useEffect(() => {
    4. // third
    5. prevCountRef.current = count;
    6. });
    7. // first
    8. const prevCount = prevCountRef.current;
    9. return (
    10. <>
    11. <h1>
    12. {/*second*/}
    13. Now: {count}, before: {prevCount}
    14. </h1>
    15. <button onClick={() => setCount(count + 1)}>+1</button>
    16. </>
    17. );

    利用useEffect中回调最后执行的特性。
    当然,我们也可以封装成一个hook:

    1. function Test(props) {
    2. const [count, setCount] = useState(0);
    3. const prevCount = usePrevious(count)
    4. return (
    5. <>
    6. <h1>
    7. Now: {count}, before: {prevCount}
    8. </h1>
    9. <button onClick={() => setCount(count + 1)}>+1</button>
    10. </>
    11. );
    12. }
    13. function usePrevious(value) {
    14. const ref = useRef();
    15. console.log('222')
    16. useEffect(() => {
    17. ref.current = value;
    18. });
    19. return ref.current;
    20. }