React 状态管理的 3 个规则

更新日期: 2020-02-05阅读: 1.9k标签: 状态

react 组件内部的状态是在渲染过程之间保持不变的封装数据。 useState() 是 React hook,负责管理功能组件内部的状态。

我喜欢 useState() ,它确实使状态处理变得非常容易。但是我经常遇到类似的问题:

  • 我应该将组件的状态划分为小状态,还是保持复合状态?
  • 如果状态管理变得复杂,我应该从组件中提取它吗?该怎么做?
  • 如果 useState() 的用法是如此简单,那么什么时候需要 useReducer()?

本文介绍了 3 条简单的规则,可以回答上述问题,并帮助你设计组件的状态。


No.1 一个关注点

有效状态管理的第一个规则是:

使状态变量负责一个问题。

使状态变量负责一个问题使其符合单一责任原则。

让我们来看一个复合状态的示例,即一种包含多个状态值的状态。

const [state, setState] = useState({
    on: true,
    count: 0
});

state.on    // => true
state.count // => 0

状态由一个普通的 JavaScript 对象组成,该对象具有 on 和 count 属性。

第一个属性 state.on 包含一个布尔值,表示开关。同样,`state.count 包含一个表示计数器的数字,例如,用户单击按钮的次数。

然后,假设你要将计数器加1:

// Updating compound state
setUser({
    ...state,
    count: state.count + 1
});

你必须将整个状态放在一起,才能仅更新 count。这是为了简单地增加一个计数器而调用的一个大结构:这都是因为状态变量负责两个方面:开关和计数器。

解决方案是将复合状态分为两个原子状态 on 和 count:

const [on, setOnOff] = useState(true);
const [count, setCount] = useState(0);

状态变量 on 仅负责存储开关状态。同样,count 变量仅负责计数器。

现在,让我们尝试更新计数器:

setCount(count + 1);
// or using a callback
setCount(count => count + 1);

count 状态仅负责计数,很容易推断,也很容易更新和读取。

不必担心调用多个 useState() 为每个关注点创建状态变量。

但是请注意,如果你使用过多的 useState() 变量,则你的组件很有可能就违反了“单一职责原则”。只需将此类组件拆分为较小的组件即可。


No.2 提取复杂的状态逻辑

将复杂的状态逻辑提取到自定义 hook 中。

在组件内保留复杂的状态操作是否有意义?

答案来自基本面(通常会发生这种情况)。

创建 React hook 是为了将组件与复杂状态管理和副作用隔离开。因此,由于组件只应关注要渲染的元素和要附加的某些事件侦听器,所以应该把复杂的状态逻辑提取到自定义 hook 中。

考虑一个管理产品列表的组件。用户可以添加新的产品名称。约束是产品名称必须是唯一的

第一次尝试是将产品名称列表的设置程序直接保留在组件内部:

function ProductsList() {
    const [names, setNames] = useState([]);  
    const [newName, setNewName] = useState('');

    const map = name => <div>{name}</div>;

    const handleChange = event => setNewName(event.target.value);
    const handleAdd = () => {    
        const s = new Set([...names, newName]);    
        setNames([...s]);  };
    return (
        <div className="products">
            {names.map(map)}
            <input type="text" onChange={handleChange} />
            <button onClick={handleAdd}>Add</button>
        </div>
    );
}

names 状态变量保存产品名称。单击 Add 按钮时,将调用 addNewProduct() 事件处理程序。

在 addNewProduct() 内部,用 Set 对象来保持产品名称唯一。组件是否应该关注这个实现细节?不需要。

最好将复杂的状态设置器逻辑隔离到一个自定义 hook 中。开始做吧。

新的自定义钩子 useUnique() 可使每个项目保持唯一性:

// useUnique.js
export function useUnique(initial) {
    const [items, setItems] = useState(initial);
    const add = newItem => {
        const uniqueItems = [...new Set([...items, newItem])];
        setItems(uniqueItems);
    };
    return [items, add];
};

将自定义状态管理提取到一个 hook 中后,ProductsList 组件将变得更加轻巧:

import { useUnique } from './useUnique';

function ProductsList() {
  const [names, add] = useUnique([]);  const [newName, setNewName] = useState('');

  const map = name => <div>{name}</div>;

  const handleChange = event => setNewName(e.target.value);
  const handleAdd = () => add(newName);
  return (
    <div className="products">
      {names.map(map)}
      <input type="text" onChange={handleChange} />
      <button onClick={handleAdd}>Add</button>
    </div>
  );
}

const [names, addName] = useUnique([]) 启用自定义 hook。该组件不再被复杂的状态管理所困扰。

如果你想在列表中添加新名称,则只需调用 add('New Product Name') 即可。

最重要的是,将复杂的状态管理提取到自定义 hooks 中的好处是:

  • 该组件不再包含状态管理的详细信息
  • 自定义 hook 可以重复使用
  • 自定义 hook 可轻松进行隔离测试


No.3 提取多个状态操作

将多个状态操作提取到化简器中。

继续用 ProductsList 的例子,让我们引入“delete”操作,该操作将从列表中删除产品名称。

现在,你必须为 2 个操作编码:添加和删除产品。处理这些操作,就可以创建一个简化器并使组件摆脱状态管理逻辑。

同样,此方法符合 hook 的思路:从组件中提取复杂的状态管理。

以下是添加和删除产品的 reducer 的一种实现:

function uniqueReducer(state, action) {
    switch (action.type) {
        case 'add':
            return [...new Set([...state, action.name])];
        case 'delete':
            return state.filter(name => name === action.name);
        default:
            throw new Error();
    }
}

然后,可以通过调用 React 的 useReducer() hook 在产品列表中使用 uniqueReducer():

function ProductsList() {
    const [names, dispatch] = useReducer(uniqueReducer, []);
    const [newName, setNewName] = useState('');

    const handleChange = event => setNewName(event.target.value);

    const handleAdd = () => dispatch({ type: 'add', name: newName });
    const map = name => {
        const delete = () => dispatch({ type: 'delete', name });    
        return (
            <div>
                {name}
                <button onClick={delete}>Delete</button>
            </div>
        );
    }

    return (
        <div className="products">
            {names.map(map)}
            <input type="text" onChange={handleChange} />
            <button onClick={handleAdd}>Add</button>
        </div>
    );
}

const [names, dispatch] = useReducer(uniqueReducer, []) 启用 uniqueReducer。 names 是保存产品名称的状态变量,而 dispatch 是使用操作对象调用的函数

当单击 Add 按钮时,处理程序将调用 dispatch({ type: 'add', name: newName })。调度一个 add 动作使 reducer uniqueReducer 向状态添加一个新的产品名称。

以同样的方式,当单击 Delete 按钮时,处理程序将调用 dispatch({ type: 'delete', name })。remove 操作将产品名称从名称状态中删除。

有趣的是,reducer 是命令模式的特例。


总结

状态变量应只关注一个点。

如果状态具有复杂的更新逻辑,则将该逻辑从组件提取到自定义 hook 中。

同样,如果状态需要多个操作,请用 reducer 合并这些操作。

无论你使用什么规则,状态都应该尽可能地简单和分离。组件不应被状态更新的细节所困扰:它们应该是自定义 hook 或化简器的一部分。

这 3 个简单的规则能够使你的状态逻辑易于理解、维护和测试。

作者:Dmitri Pavlutin
翻译:疯狂的技术
原文:https://dmitripavlutin.com/


链接: https://fly63.com/article/detial/8142

Javascript 状态管理工具 DataSet ,实现数据的订阅、查询、撤销和恢复

网页是用户与网站对接的入口,当我们允许用户在网页上进行一些频繁的操作时,对用户而言,误删、误操作是一件令人抓狂的事情,“如果时光可以倒流,这一切可以重来……”。

理解 React 轻量状态管理库 Unstated

在React写应用的时候,难免遇到跨组件通信的问题。现在已经有很多的解决方案。React本身的Context,Redux结合React-redux,Mobx结合mobx-react

你再也不用使用 Redux、Mobx、Flux 等状态管理了

这个库的作者希望使用 React 内置 API ,直接实现状态管理的功能。看完这个库的说明后,没有想到代码可以这个玩。短短几行代码,仅仅使用 React Hooks ,就实现了状态管理的功能。

为什么要使用状态管理

我们平时开发的大部分项目,由于复杂度不够, 很少使用 Vuex、Redux 等状态管理库,就算引入了 Vuex 这些库,也只是当作一个全局数据引用,并非对应用状态进行管理。但一旦页面的复杂度比较高,必然要引入状态管理,今天就聊聊我理解中的状态管理。

React使用Hooks与Context替代Redux状态管理

React Hooks 在 2018 年年底就已经公布了,正式发布是在 2019 年 5 月,关于它到底能做什么用,并不在本文的探讨范围之内,本文旨在摸索,如何基于 Hooks 以及 Context,实现多组件的状态共享,完成一个精简版的 Redux。

如何使用react hooks来进行状态管理?

首先要明确为什么要使用redux,这一点很重要,如果不知道为什么使用redux,那么在开发的过程中肯定不能合理的使用redux.首先来看redux的本质:redux做为一款状态管理工具,主要是为了解决组件间通信的问题。

Flutter基础--状态管理

当我们使用编译器创建一个新Flutter应用的时候,我们可以在主界面看到两个小部件StatelessWidget和StatefulWidget。这是两个最常见使用最频繁的小部件了。StatelessWidget ,StatefulWidget

共享可变状态中出现的问题以及如何避免?

本文回答了以下问题:么是共享可变状态?为什么会出现问题?如何避免其问题?标有(高级)的部分会更深入,如果你想更快地阅读本文,可以跳过。

使用Observable实现Vue全局状态共享

项目不大, 又不想用Vuex, 那么使用Observable来实现状态共享也不失为一个选择。用法 :让一个对象可响应。Vue 内部会用它来处理 data 函数返回的对象

node如何实现保持登录状态?

当我们登录成功,在这个页面刷新,页面并没有保存登录状态;今天我们就来看一下如何在后台使用cookie保存用户登录状态。做到刷新页面仍然显示在用户登录界面。node实现保持登录状态的方法如下:

点击更多...

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!