useState 原理

2022-06-04  本文已影响0人  bestCindy

首先回顾一下 useState 的用法

import React, { useState } from 'react';

function App() {
  const [n, setN] = useState(0);

  return (
    <div>
      <button onClick={() => setN(n+1)}>Click me</button>
      {n}
    </div>
  );
}

export default App;

我们来实现 useMyState

let _state;
const useMyState = (intiState) => {
  _state = _state ?? intiState;
  const setState = (newState) => {
    _state = newState;
    render();
  }
  return [_state, setState];
}

const render = () => ReactDOM.render(<App />, document.getElementById('root'));

function App() {
  const [n, setN] = useMyState(0);
  return (
    <div>
      <p>{n}</p>
      <button onClick={() => setN(n+1)}>Click me</button>
    </div>
  )
}

render();

有多个 state 的情况呢

// 这就是为什么 React 不允许在 if...else... 里面调用 hooks
let _state = [];
let index = 0;

const useMyState = (intiState) => {
  let currentIndex = index;

  _state[currentIndex] = _state[currentIndex] ?? intiState;
  const setState = (newState) => {
    _state[currentIndex] = newState;
    index = 0;
    render();
  }

  index+=1;
  return [_state[currentIndex], setState];
}

const render = () => ReactDOM.render(<App />, document.getElementById('root'));

function App() {
  const [n, setN] = useMyState(0);
  const [m, setM] = useMyState(0);

  return (
    <div>
      <p>{n}</p>
      <button onClick={() => setN(n+1)}>Click me</button>
      <p>{m}</p>
      <button onClick={() => setM(n+1)}>Click me</button>
    </div>
  )
}

render();

useState 中的闭包问题

由于 React Hooks 是函数式组件,在异步操作或者使用 useCallBackuseEffectuseMemo的时候会形成闭包

举个例子

import React, { useState } from 'react';

function Demo() {
  const [num, setNum] = useState(0);

  const handleClick = () => {
    setTimeout(() => {
      alert(num)
    }, 3000)
  };

  return (
    <div>
      <div>当前点击了{num}次</div>
      <button onClick={() => { setNum(num + 1)}}>点我</button>
      <button onClick={handleClick }>展示现在的值</button>
    </div>
  )
}

export default Demo;

现在我们:

  1. 点击 “点我” 增加到 1
  2. 点击 “展示现在的值”
  3. alert 之前,点击 “点我”,让 num 增加到 2

结果是这样的:

现在用 class 组件 refactor 一编

class Demo extends React.Component {
  stete = {
    num: 0
  }

  handleClick = () => {
    setTimeout(() => {
      alert(this.state.num);
    }, 3000)
  }

  render() {
    const { num } = this.state;
    return (
      <div>
        <div>当前点击了{num}次</div>
        <button onClick={() => { this.setState({ num : num + 1}) }}>点我</button>
        <button onClick={this.handleClick }>展示现在的值</button>
      </div>
    )
  }
}

重复之前的步骤:

从这个例子可以看出,React Hooks 在某一个特定渲染中 stateprops 是与其绑定的,然而类组件并不是

解决 useState 中的闭包问题

function Demo() {
  const nRef = React.useRef(0); // nRef 是一个对象 { current: 0 }

  const handleClick = () => {
    setTimeout(() => {
      alert(nRef.current);
    }, 3000);
  }

  return (
    <div>
      <p>当前点击了{nRef.current}次</p>
      <button onClick={() => (nRef.current += 1)}>点击</button>
      <button onClick={handleClick}>展示现在的值</button>
    </div>
  )
}

我们用 nRef 解决了闭包的问题,但是现在有另一个问题,UI 并不会重新渲染

这时候要用到 useState 触发更新


function Demo() {
  const nRef = React.useRef(0); // nRef 是一个对象 { current: 0 }
  const update = React.useState()[1];

  const handleClick = () => {
    setTimeout(() => {
      alert(nRef.current);
    }, 3000);
  }

  return (
    <div>
      <p>当前点击了{nRef.current}次</p>
      <button onClick={() => ((nRef.current += 1), update(nRef.current))}>点击</button>
      <button onClick={handleClick}>展示现在的值</button>
    </div>
  )
}
上一篇 下一篇

猜你喜欢

热点阅读