React 新版官方文档 (二) useState 用法详解

React 新版官方文档 (二) useState 用法详解_第1张图片

背景

本文默认读者对 useState 有最为基本的了解,比如知道他的写法应当是怎样的,下面着重介绍部分重要的、在开发过程中会踩的坑和一些特性,最后动手实现一个最基本的 useState 代码

useState

⭐️ 注意事项:

  1. 状态只在下次更新时异步变化,如果在设置状态后立即读取值还是老状态的

  2. 如果提供的新状态与当前状态相同 (使用 Object.is 比较),将跳过重新渲染组件以及子组件

  3. React 是批量合并更新 state 的。多个状态更新操作会被放入一个队列中,然后在适当的时机进行合并和批量处理。这可以防止在单个事件期间多次重新渲染,一定会按照顺序。在极少数情况下,需要强制 React 提前更新界面,例如访问 DOM,可以使用 flushSync。
    例子:

  4. 设置状态后立即读取状态

  5. 使用 setState 并不会改变已执行代码的当前状态 只会影响从下一次渲染开始返回的内容,想获取到变化后的值,可以选用 useEffect
    function handleClick() {
    setName(‘Robin’);
    console.log(name); // Still “Taylor”!
    }

  6. 基于上一次状态更新值
    注意 setState 特性
    function handleClick() {
    // ❌
    setAge(age + 1); // setAge(42 + 1)
    setAge(age + 1); // setAge(42 + 1)
    setAge(age + 1); // setAge(42 + 1)

    // ✅
    setAge(a => a + 1); // setAge(42 => 43)
    setAge(a => a + 1); // setAge(43 => 44)
    setAge(a => a + 1); // setAge(44 => 45)
    }

  7. 更新状态中的对象和数组
    应该替换它而不是修改它
    React 中状态应该是只读的
    // Don’t mutate an object in state like this:
    form.firstName = ‘Taylor’;
    // ✅ Replace state with a new object
    setForm({
    …form,
    firstName: ‘Taylor’
    });

  8. 避免重新创建初始状态
    createInitialTodos 有效执行只有组件挂载时一次,但在之后组件历次re-render时都会被执行,这就造成了资源浪费 只需要传递初始化函数声明就行
    function TodoList() { // ❌
    const [todos, setTodos] = useState(createInitialTodos());
    // …
    function TodoList() { // ✅
    const [todos, setTodos] = useState(createInitialTodos);
    // …
    其他问题:https://react.dev/reference/react/useState#troubleshooting

动手实现一个 useState

// 存储状态的数组
let state = []
// 存储更改状态方法的数组
let setters = []
// 用来记录状态和更改状态方法对应关系的下标
let stateIndex = 0

function createSetter(index) {
  return function (newState) {
    state[index] = newState
    render()
  }
}

function useState(initialState) {
  state[stateIndex] = state[stateIndex] ? state[stateIndex] : initialState
  // 采用闭包缓存每个state对应的setState
  setters.push(createSetter(stateIndex))
  const value = state[stateIndex]
  const setter = setters[stateIndex]
  // 每创建完一组都要+1,用来作为下一组状态的索引
  stateIndex++
  return [value, setter]
}

// 因为状态更改要刷新视图,因此这里用ReactDom.render方法来模拟更改状态后刷新视图的操作
function render() {
  // 每次调用render都要重置stateIndex,否则对应的索引无限递增将无法正确匹配state和setState之间的关系
  stateIndex = 0
  ReactDom.render(<App />, document.getElementById('root'))
}


你可能感兴趣的:(react.js,javascript,前端,前端框架,typescript)