Skip to content

React 模板更换全局状态库

  • React 模板之前采用的是 Mobx 状态库,这个库可以更加简答的修改状态,使用简单,所以之前采用这个库作为全局状态库;

  • 但 Mobx 库使用起来更加的像 Vue ,为了更加的 ’React‘ ,更换为 Recoil ,理由如下:

    1. 官方支持:Recoil 是由 Facebook 推出的状态管理库,因此在 React 生态系统中得到了官方支持。它与 React 的紧密集成使得在 React 项目中使用 Recoil 更加自然和无缝。

    2. 原子状态管理:Recoil 提供了原子状态管理的概念,可以更细粒度地管理应用程序的状态。通过使用原子状态,你可以将状态分解为单独的原子单元,使状态更新更加可控和可预测。这种粒度更小的状态管理可以提高代码的可维护性和可测试性。

    3. 基于钩子的 API:Recoil 提供了基于钩子的 API,如 useRecoilState 和 useRecoilValue,使得在组件中访问和更新状态非常方便。这些钩子与 React 的函数式组件风格完美结合,使得状态管理的代码更加简洁和易读。

    4. 惰性计算:Recoil 引入了惰性计算的概念,可以定义派生状态(derived state),这些状态根据其他状态的变化而自动计算更新。这样可以避免手动编写繁琐的派生状态更新逻辑,使得状态之间的依赖关系更加清晰和自动化。

    5. TypeScript 支持:Recoil 提供了强大的 TypeScript 支持,包括类型推断和类型安全。在使用 Recoil 进行状态管理时,你可以获得更好的类型检查和 IDE 支持,帮助你在开发过程中捕获潜在的错误和提高代码的可靠性。

  • Recoil 库同样使用起来很简单,上手简单,和使用 useState 基本一样,所以没有什么使用上的压力。

  • 官方文档

Recoil 快速上手

  • RecoilRoot 包裹根目录
jsx
import React from "react"
import { RecoilRoot, atom, selector, useRecoilState, useRecoilValue } from "recoil"

function App() {
  return (
    <RecoilRoot>
      <CharacterCounter />
    </RecoilRoot>
  )
}
  • 定义 Atom 状态
  • 一个 atom 代表一个状态。Atom 可在任意组件中进行读写。读取 atom 值的组件隐式订阅了该 atom,因此任何 atom 的更新都将致使使用对应 atom 的组件重新渲染:
js
const textState = atom({
  key: "textState", // unique ID (with respect to other atoms/selectors)
  default: "", // default value (aka initial value)
})
  • 在需要使用的组件中,你应该引入并使用 useRecoilState(),如下所示:
jsx
function CharacterCounter() {
  return (
    <div>
      <TextInput />
      <CharacterCount />
    </div>
  )
}

function TextInput() {
  const [text, setText] = useRecoilState(textState)

  const onChange = (event) => {
    setText(event.target.value)
  }

  return (
    <div>
      <input type="text" value={text} onChange={onChange} />
      <br />
      Echo: {text}
    </div>
  )
}
  • 定义 Selector 状态
  • selector 代表一个派生状态,派生状态是状态的转换。你可以将派生状态视为将状态传递给以某种方式修改给定状态的纯函数的输出:
jsx
const charCountState = selector({
  key: "charCountState", // unique ID (with respect to other atoms/selectors)
  get: ({ get }) => {
    const text = get(textState)

    return text.length
  },
})
  • 我们可以使用 useRecoilValue() 的 hook,来读取 charCountState 的值:
jsx
function CharacterCount() {
  const count = useRecoilValue(charCountState)

  return <>Character Count: {count}</>
}
  • 在非组件中更改状态,由于官方并没有提供这种方案,所以只能寻求三方了,有一个库 recoil-nexus 解决了这个问题
jsx
import React from "react";
import { RecoilRoot } from "recoil";
import RecoilNexus from "recoil-nexus";

export default function App() {
  return (
    <RecoilRoot>
      <RecoilNexus />

      {/* ... */}
    </RecoilRoot>
  );
}

export default App;
jsx
// Loading example
import { loadingState } from "../atoms/loadingState"
import { getRecoil, setRecoil } from "recoil-nexus"

export default function toggleLoading() {
  const loading = getRecoil(loadingState)
  setRecoil(loadingState, !loading)
}
MethodReturns
getRecoilgetter function
getRecoilPromisegetter function, returns a promise. To be used with asynchronous selectors.
setRecoilsetter function, pass value to be set as second parameter
resetRecoilpass atom as parameter to reset to default value

添加别名

  • 为方便日常项目开发,在两个模板中添加了别名,用来更加快捷的开发工作;
  • vite.config.js中添加如下配置:
js
resolve: {
  alias: {
    '@': '/src',
  },
},
  • 添加别名后有两个问题,一个是路径智能提示没有了,还有一个是设置别名后不能用 Ctrl+鼠标左键点击路径,快速跳转到外部文件了

  • vscode 设置文件中添加如下配置,可以解决路径智能提示问题;

json
{
  // 其他配置
  "path-intellisense.mappings": { "@": "${workspaceRoot}/src" }
}
  • 项目模板根目录添加了 jsconfig.json 文件,并添加如下配置,可以解决不能用 Ctrl+鼠标左键点击路径,快速跳转到外部文件
js
// 解决带alias快捷跳转失败
{
  "compilerOptions": {
    "target": "ES6",
    "jsx": "react",
    "baseUrl": "./",
    "paths": {
      // ?此处添加别名路径
      "@/*": ["src/*"]
    }
  },
  "exclude": ["node_modules", "dist"],
  "include": ["src/**/*"]
}