在 React 中我们对状态管理的方式有很多,例如 state、props、context 和 Redux 等来对状态进行管理。在编写项目的时候我们可以根据自己的习惯和对 API 掌握的情况来选择状态的管理 API,这样在编码的形式上比较灵活和增强了可变性。
但是随之而来的问题就是可能项目的代码难以维护和设计出不好的代码架构。所以这篇文章主要是聊聊我个人对状态管理的想法。
就近原则使用本地储存状态
就近原则使用本地储存状态就是尽量让储存状态在本地组件中声明和使用。具体的例子如下:
const [count, setCount] = useState(0);
return (
<button onClick={() => setCount((prevCount) => prevCount + 1)}>
您点击了 {count} 次
</button>
);
上面的例子中我们只是声明了一个 count 的本地储存状态,并且只是简单的进行了加 1 的操作。假如我们的业务需求需要添加一个重置的操作的话,我们只要这样做就可以:
const [count, setCount] = useState(0);
return (
<>
<button onClick={() => setCount((prevCount) => prevCount + 1)}>
您点击了 {count} 次
</button>
<button onClick={() => setCount(0)}>重置</button>
</>
);
结合两个例子来看,我们采用就近原则来定义和声明本地储存状态后,代码的可读性和扩展性都比较不错。
接下来我们来说说我们可能在开发中会出现的情况,例如:
const Demo: React.FC = () => {
const [count, setCount] = useState(0);
const incrementCount = () => setCount((prevCount) => prevCount + 1);
return (
<>
<Counter count={count} incrementCount={incrementCount} />
</>
);
};
const Counter: React.FC<{ count: number; incrementCount: VoidFunction }> = ({
count,
incrementCount,
}) => {
return <button onClick={incrementCount}>你点击了 {count} 次</button>;
};
上面的代码中我们采用 props 进行参数的传递而进行数据状态的管理,而当需要添加重置业务的时候,我们的代码会变成这样:
const Demo: React.FC = () => {
const [count, setCount] = useState(0);
const incrementCount = () => setCount((prevCount) => prevCount + 1);
const resetCount = () => setCount(0);
return (
<>
<Counter
count={count}
incrementCount={incrementCount}
resetCount={resetCount}
/>
</>
);
};
const Counter: React.FC<{
count: number;
incrementCount: VoidFunction;
resetCount: VoidFunction;
}> = ({ count, incrementCount, resetCount }) => {
return (
<>
<button onClick={incrementCount}>你点击了 {count} 次</button>
<button onClick={resetCount}>重置</button>
</>
);
};
从上面的代码中我们可以看到业务只是简单添加一个重置功能,我们需要对父组件和子组件都进行修改,这样在对面维护和可读性上都是比较差的,所以我们以后在开发的时候尽量采用就近原则来声明和使用本地存储状态。
何时在父组件中使用存储状态
在我们的业务代码中,组件并不可能只有一个,而是会有父子关系。所以当出现父子组件的情况下,我们可以先将子组件需要的储存状态放在本地组件中,随着组件功能完全实现后,我们可以把通用的储存状态移到父组件中。
例如我们的业务组件结构如下:
<TodoContainer>
<TodoList></TodoList>
<TodoCount></TodoCount>
</TodoContainer>
在上述的业务组件中,我们可以假设 TodoList 组件和 TodoCount 都会用到同一个数据,即 todos,所以我们可以在 TodoContainer 组件中声明 todos 储存状态,这样在父组件中统一管理后,代码的可读性和维护性就会大大加强。具体代码实例代码如下:
function TodosContainer() {
const [todos, setTodos] = useState([]);
return (
<>
<TodoList todos={todos} />
<TodoCount todos={todos} />
</>
);
}
何时使用全局存储状态
全局存储状态主要分为系统级全局存储状态和业务级全局存储状态。
系统级全局状态主要是存储系统经常用到的一些信息,例如登录用户的信息等。
业务级全局存储状态主要是存储业务流程中经常用到的一些信息,例如课程业务流程中我们可能很多组件中会用到课程信息,所以我们就可以把课程信息作为业务级的全局存储状态来定义和使用。