由于React Hooks已经发布,函数组件可以使用state(状态)和effect(副作用)。React 中有两个用于管理状态的钩子:useState 和 useReducer。本教程将逐步介绍 React 中的 useState 示例,以帮助您开始使用此 React Hook 进行状态管理。
React中的简单状态
过去,状态不能在函数组件中使用。因此称它们为功能性无状态组件。然而,随着 React Hooks 的发布,状态也可以部署在这类组件中,因此它们被 React 社区重新命名为函数组件。以下示例演示了如何通过 useState 钩子在函数组件中使用状态的简单示例:
const App = () => {
const [count, setCount] = React.useState(0);
const handleIncrease = () => {
setCount(count + 1);
};
const handleDecrease = () => {
setCount(count - 1);
};
return (
<div>
Count: {count}
<hr />
<div>
<button type="button" onClick={handleIncrease}>
Increase
</button>
<button type="button" onClick={handleDecrease}>
Decrease
</button>
</div>
</div>
);
};
useState 函数将初始状态的值作为参数。在这种情况下,计数从 0 开始。此外,钩子返回一个包含两个值的数组:count
和setCount
。命名这两个值取决于您,因为它们是从允许重命名的返回数组中解构的。
在这种情况下count
,第一个值表示当前状态。在这种情况下setCount
,第二个值是一个函数,用于在调用它时使用传递给此函数的任何内容来更新状态。该函数也称为状态更新函数。每次调用此函数时,React 都会重新渲染组件以渲染最近的状态。
React中的复杂状态
到目前为止,该示例仅显示了带有 JavaScript 原语的 useState。这就是 useState 的亮点所在。它可用于整数、布尔值、字符串以及数组。但是,一旦您计划使用对象或更复杂的数组来管理更复杂的状态,您应该查看React 的 useReducer。在多种情况下,useReducer 的性能优于 useState:
- 复杂状态容器
- 复杂的状态转换
- 条件状态更新
它还有助于通过仅使用 useState 来避免多次连续的状态更新。如果你想在 React 中管理更复杂的状态,你绝对应该检查一下。
社区还有一个库叫use-immer,在开发中也常使用。
React中的异步状态
如果您依赖实际状态来更新状态会发生什么?让我们通过一个示例来说明这种情况,我们使用JavaScript 内置的 setTimeout函数延迟状态更新:
const App = () => {
const [count, setCount] = React.useState(0);
const handleIncrease = () => {
setTimeout(() => setCount(count + 1), 1000);
};
const handleDecrease = () => {
setTimeout(() => setCount(count - 1), 1000);
};
return (
<div>
Count: {count}
<hr />
<div>
<button type="button" onClick={handleIncrease}>
Increase
</button>
<button type="button" onClick={handleDecrease}>
Decrease
</button>
</div>
</div>
);
};
每次单击其中一个按钮时,都会延迟一秒调用状态更新函数。只需单击一下即可。但是,请尝试连续多次单击其中一个按钮。状态更新功能将始终count
在这一秒内以相同的状态运行。为了解决这个问题,您可以向useState传递一个状态更新函数:
import React from 'react';
const App = () => {
const [count, setCount] = React.useState(0);
const handleIncrease = () => {
setTimeout(() => setCount(state => state + 1), 1000);
};
const handleDecrease = () => {
setTimeout(() => setCount(state => state - 1), 1000);
};
return (
<div>
Count: {count}
<hr />
<div>
<button type="button" onClick={handleIncrease}>
Increase
</button>
<button type="button" onClick={handleDecrease}>
Decrease
</button>
</div>
</div>
);
};
export default App;
该函数为您提供执行该函数时的状态。这样,您永远不会在任何陈旧状态下操作。因此,一个好的经验法则是:如果您的状态更新取决于您之前的状态,请始终使用传递函数的写法。
React 的 useState 是管理状态的首选钩子。它可以与 useReducer 和 useContext 一起用于 React 中的现代状态管理。与 useReducer 相比,它是管理状态的更轻量级的方法。
如果项目不复杂,就使用hooks管理状态就足够了。如果项目过于庞大,数据结构很复杂,那还是建议使用三方库做状态托管。比如成熟的:React-Redux、mobx、dva。
何时使用useState或useReducer
开始使用 React Hooks 的每个人都会很快了解 useState 钩子。通过设置初始状态并返回实际状态和更新函数来更新功能组件中的状态:
import React, { useState } from 'react';
const Counter = () => {
const [count, setCount] = useState(0);
const handleIncrease = () => {
setCount(count => count + 1);
};
const handleDecrease = () => {
setCount(count => count - 1);
};
return (
<div>
<h1>Counter with useState</h1>
<p>Count: {count}</p>
<div>
<button type="button" onClick={handleIncrease}>
+
</button>
<button type="button" onClick={handleDecrease}>
-
</button>
</div>
</div>
);
};
export default Counter;
useReducer 钩子也可用于更新状态,但它以更复杂的方式进行:它接受一个 reducer 函数和一个初始状态,并返回实际状态和一个调度函数。dispatch 函数通过将动作映射到状态转换以隐式方式改变状态:
import React, { useReducer } from 'react';
const counterReducer = (state, action) => {
switch (action.type) {
case 'INCREASE':
return { ...state, count: state.count + 1 };
case 'DECREASE':
return { ...state, count: state.count - 1 };
default:
throw new Error();
}
};
const Counter = () => {
const [state, dispatch] = useReducer(counterReducer, { count: 0 });
const handleIncrease = () => {
dispatch({ type: 'INCREASE' });
};
const handleDecrease = () => {
dispatch({ type: 'DECREASE' });
};
return (
<div>
<h1>Counter with useReducer</h1>
<p>Count: {state.count}</p>
<div>
<button type="button" onClick={handleIncrease}>
+
</button>
<button type="button" onClick={handleDecrease}>
-
</button>
</div>
</div>
);
};
export default Counter;
从上可以看出两者的不同之处,所以从经验总结:
如果您有以下情况,请使用 useState:
- A) JavaScript 原语作为状态
- B) 简单的状态转换
- C) 组件内的业务逻辑
- D) 不同的属性不会以任何相关的方式发生变化,并且可以由多个 useState 钩子管理
- E)与您的组件共存的状态
- F)一个小应用程序(但这里的线条模糊)
如果您有以下情况,请使用 useReducer:
- A) JavaScript 对象或数组作为状态
- B) 复杂的状态转换
- C)复杂的业务逻辑更适合reducer功能
- D) 应在一个状态对象中管理的不同属性捆绑在一起
- E) 需要在组件树的深处更新状态
- F)一个中型应用程序(注意:这里的线条模糊)
- G) 需要更轻松地测试它
- H) 需要更可预测和可维护的状态架构
参考资料: