likes
comments
collection
share

在 React 中应避免的 4 个 useState 使用错误 🚫

作者站长头像
站长
· 阅读数 48

文章翻译自 Medium 上一篇高赞文章 levelup.gitconnected.com/4-usestate-…

内容虽然简单,但却是平时开发的时候容易忽略的内容。Enjoy!!

错误一:忘记考虑先前状态 😨

在使用 React 的 useState Hook 时,一个常见的错误是不考虑最近的状态更新。这种疏忽可能导致意外行为,尤其是在处理快速或多次状态更新时。

问题说明 假设你正在用 React 构建一个计数器,你的目标是每次点击按钮时增加计数器的值。一个简单的方法可能是将当前状态值加 1。然而,这种方法可能会出现问题。

import React, { useState } from 'react';

const CounterComponent = () => {
  const [counter, setCounter] = useState(0);

  const incrementCounter = () => {
    setCounter(counter + 1); // 可能不会按预期工作
  };

  return (
    <div>
      <p>Counter: {counter}</p>
      <button onClick={incrementCounter}>Increment</button>
    </div>
  )
};

export default CounterComponent;

在上面的代码中,incrementCounter 基于当前值更新计数器。这看起来很简单,但可能会出现问题。React 可能会将多个 setCounter 调用批量处理,或者其他状态更新可能会干扰,导致计数器未能每次都正确更新。

解决方案 为了避免这个问题,使用 setCounter 方法的函数形式。这个版本接受一个函数作为参数,React 会调用这个函数并传递最近的状态值。这确保了你总是在处理最新的状态值。

import React, { useState } from 'react';

const CounterComponent = () => {
  const [counter, setCounter] = useState(0);

  const incrementCounter = () => {
    setCounter(prevCounter => prevCounter + 1); // 正确地基于最新状态更新
  };

  return (
    <div>
      <p>Counter: {counter}</p>
      <button onClick={incrementCounter}>Increment</button>
    </div>
  );
};

export default CounterComponent;

在修正后的代码中,incrementCounter 使用一个函数来更新状态。这个函数接收最近的状态 (prevCounter) 并返回更新后的状态。这种方法更加可靠,特别是在快速或连续发生多次更新时。

错误二:忽视状态不可变性 🧊

问题说明 在 React 中,状态应视为不可变。一个常见的错误是直接修改状态,尤其是在处理对象和数组等复杂数据结构时。

看看下面这个错误的方法:

import React, { useState } from 'react';

const ProfileComponent = () => {
  const [profile, setProfile] = useState({ name: 'John', age: 30 });

  const updateAge = () => {
    profile.age = 31; // 直接修改了状态
    setProfile(profile);
  };

  return (
    <div>
      <p>Name: {profile.name}</p>
      <p>Age: {profile.age}</p>
      <button onClick={updateAge}>Update Age</button>
    </div>
  );
};

export default ProfileComponent;

这段代码错误地直接修改了 profile 对象。这样的修改不会触发重新渲染,从而导致不可预测的行为。

解决方案 在更新状态时总是创建一个新的对象或数组,以保持状态的不可变性。可以使用扩展运算符来实现。

import React, { useState } from 'react';

const ProfileComponent = () => {
  const [profile, setProfile] = useState({ name: 'John', age: 30 });

  const updateAge = () => {
    setProfile({ ...profile, age: 31 }); // 正确地更新了状态
  };

  return (
    <div>
      <p>Name: {profile.name}</p>
      <p>Age: {profile.age}</p>
      <button onClick={updateAge}>Update Age</button>
    </div>
  );
};

export default ProfileComponent;

在修正后的代码中,updateAge 使用扩展运算符创建一个新的 profile 对象,并更新了 age,从而保持状态的不可变性。

错误三:误解异步更新 ⏳

问题说明 React 中通过 useState 的状态更新是异步的。这通常会引起混淆,尤其是在快速连续进行多次状态更新时。开发者可能期望状态在 setState 调用后立即改变,但实际上,React 为了性能原因会批量处理这些更新。

让我们看看这种误解在常见场景下会造成什么问题:

import React, { useState } from 'react';

const AsyncCounterComponent = () => {
  const [count, setCount] = useState(0);

  const incrementCount = () => {
    setCount(count + 1);
    setCount(count + 1);
    // 开发者期望 count 会增加两次
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={incrementCount}>Increment Count</button>
    </div>
  );
};

export default AsyncCounterComponent;

在这个例子中,开发者打算将 count 增加两次。然而,由于状态更新的异步性,两个 setCount 调用基于相同的初始状态,导致 count 只增加了一次。

解决方案 要正确处理异步更新,使用 setCount 的函数更新形式。这样可以确保每次更新都是基于最新的状态。

import React, { useState } from 'react';

const AsyncCounterComponent = () => {
  const [count, setCount] = useState(0);

  const incrementCount = () => {
    setCount(prevCount => prevCount + 1);
    setCount(prevCount => prevCount + 1);
    // 现在每次更新都会正确地依赖于最新的状态
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={incrementCount}>Increment Count</button>
    </div>
  );
};

export default AsyncCounterComponent;

在上面的代码中,每次调用 setCount 都使用状态的最新值,确保准确和顺序的更新。这种方法对依赖当前状态的操作尤为重要,尤其是在快速连续发生多次状态更新时。

错误四:乱用状态 📊

问题说明 一个常见的错误是将可以从现有状态或属性派生的数据存储在状态中。这样的冗余状态会导致代码复杂且容易出错。

例如:

import React, { useState } from 'react';

const GreetingComponent = ({ name }) => {
  const [greeting, setGreeting] = useState(`Hello, ${name}`);

  return (
    <div>{greeting}</div>
  );
};

export default GreetingComponent;

在这里,greeting 状态是不必要的,因为它可以直接从 name 属性派生。

解决方案 不要使用状态,而是直接从现有状态或属性派生数据。

import React from 'react';

const GreetingComponent = ({ name }) => {
  const greeting = `Hello, ${name}`; // 直接从属性派生

  return (
    <div>{greeting}</div>
  );
};

export default GreetingComponent;

在修正后的代码中,greeting 直接从 name 属性计算得出,简化了组件,避免了不必要的状态管理。

结论 🚀

有效地使用 React 中的 useState Hook 对于构建可靠和高效的应用程序至关重要。通过理解并避免常见错误,如忽略先前状态、错误处理状态不可变性、忽视异步更新以及避免对派生数据使用冗余状态,你可以确保组件行为更加顺畅和可预测。记住这些见解,以提升你的 React 开发旅程,创建更稳健的应用程序。

Happy coding!!

转载自:https://juejin.cn/post/7398539313645338635
评论
请登录