优秀的编程知识分享平台

网站首页 > 技术文章 正文

2021系列——在React中更好地处理状态的几种方法

nanyue 2024-10-01 13:12:54 技术文章 10 ℃

React使开发人员可以完全自由地在组件中管理状态。React具有两种类型的组件:类组件和功能组件,他们是在React v16中引入的。

类组件使用的方法被用来管理状态,如:this.state和this.setState针对状态,组件在被安装后使用componentDidMount()运行副作用。

对于上面的内容,你如果有兴趣,可以阅读有关这些方法的其它信息:

相关链接:https://reactjs.org/docs/state-and-lifecycle.html

但是,由于大部分React开发人员目前都在使用React功能组件,因此本文我们会专注在功能组件及其状态,这些功能是通过React钩子进行管理的:

相关链接:https://reactjs.org/docs/hooks-intro.html

在本文,我们会介绍一些近期发现的最佳实践(或许你已经了解,那么请忽略本文),这些最佳实践用于管理功能组件中的状态,并真正意义上利用了React钩子API,下面就让我们开始!


1 . 把useReducer用于复杂状态

useReducer钩子是功能强大的React钩子,用于处理不需要第三方依赖项的复杂状态管理,并且它减少了每次渲染时重新创建的数据量。但有时,useState钩子解决不了问题,尤其是在处理涉及大型对象的复杂状态行为时。

当把环境和Typescript结合使用时,useReducer的功能会非常强大。

让我们看一下如何在现实中利用上它:

const BasicComponent = () => {
  const [isOpen, setIsOpen] = useState(false);
  const [name, setName] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState(null);
  const [id, setid] = useState('');
  
  return (
    // ...  
  )
}

// Refactored this is a lot easier to read, more modular, and faster!

const initialState = {
  isOpen: false,
  name: '',
  isLoading: false,
  error: null,
  id: ''
}

const reducer = (state, action) => {
  switch (action.type) {
    case 'setIsOpen':
      return { ...state, isOpen: action.payload }
      break;
    // ... 
    default:
      return state;
  }
}

const BetterComponent = () => {
  const [state, dispatch] = useReducer(reducer, initialState);
  
  return (
    // ...  
  )
}


2 . Custom Hooks

当使用React钩子时,有可能会在一个组件中出乎预料获得复杂状态逻辑,该组件利用多种类型的钩子来实现一个目的。

还好,我们是可以制作自定义的React钩子的,以便把复杂的逻辑包含在单个可访问的钩子中;而这对于表单、切换、异步行为以及其他在组件中引发一堆钩子的情况会非常有用。

下面来看一个自定义钩子的示例,以帮助在项目列表中切换项目,代码如下:

import { useState } from 'react';

type UseToggle<T> = [T[], (item: any) => void, (items?: T[]) => void];

const useToggle = <T = any>(defaultValues?: T[]): UseToggle<T> => {
  const [items, setItems] = useState<T[]>(defaultValues || []);

  const toggleItem = (item: any) => {
    if (items.includes(item)) {
      setItems((prev) => prev.filter((i) => i !== item));
    } else {
      setItems((prev) => prev.concat(item));
    }
  };

  const reset = (newItems?: T[]) => setItems(newItems || []);

  return [items, toggleItem, reset];
};

export default useToggle;

通过将所有这些逻辑抽象到自定义的React钩子中,这不仅易于阅读,而且可在我们的应用中重复使用!


3 . 全局状态管理

恐怕多数情况下,你不需要状态管理库,实际上仅仅会在处理复杂状态的大型应用中才可能会面对需要引入外部库进行管理的情况,即便在这种情况下,如果的确不能仅仅以Context API为条件在组件间共享数据,还需要慎重考虑一下。

假设你已经决定需要外部状态管理工具,我们会建议查看一下Recoil,它是用于管理全局状态的超轻量级简便工具,而另一个受欢迎的库是Redux。(关于如何有效使用这些工具,本文先不做探讨)


4 . 使用数据提取库

从外部API提取数据,这貌似是一个简单的问题,但是一旦你需要在内存中缓存数据(以减少API调用的次数),对其进行更新并在多个位置进行访问,就会变得非常复杂。

值得庆幸的是,有诸如React Query之类的现代数据获取库,

相关链接:https://react-query.tanstack.com/

可以有效地从外部源获取、缓存、作废和刷新数据;还可以把它们用于把数据发送到某些外部客户端,以便真正涵盖与服务器交互的整个工作流程。

更棒的是,其中一些库譬如Apollo Client,

相关链接:https://www.apollographql.com/docs/react/

包含了状态管理,能够以可预测、声明和可读的方式处理包括获取数据、数据缓存以及自动更新UI的完整过程。

结论

管理React状态可能会很复杂,但是我希望借助这些技巧,以高效且易读的方式进行操作。

最近发表
标签列表