数据流管理、状态管理库:flux、redux、mobx、dva、vuex、zustand、valtio、recoil、jotai

一、为什么需要使用数据流管理、状态管理容器?

数据流管理、状态管理

1、随着单页面应用的兴起,JavaScript 需要管理比任何时候都要多的状态。可能包括服务器响应、缓存数据、UI 状态等。

2、状态管理其实就是页面 UI 跟 Javascript 变量的同步,也涉及跟服务端数据的同步。

“无框架时代”之前的状态管理

在web 前端还处于“无框架时代”之前,很多系统的网页用着纯 Javascript 或者 jQuery 来做一些状态管理,比如:

// 获取用户信息
feth('/getUserInfo').then(res => {
	// 展示到页面
	const nameDiv= document.getElementById('name');
	nameDiv.innerText = res.name;
})

上面的代码展示了,如何获取远程数据,并查找到目标元素Dom,并用Javascript 或者 jQuery 操作Dom节点进行内容更新。但在大型的web应用中,涉及到复杂场景,这个操作就会变得很复杂,稍不注意就会有各种“局部刷新”引起的BUG。

MVC、MVVM架构模式下的数据流、状态管理

1、MVC架构模式(Model View Controller 模型-视图-控制器),组件内部进行了页面UI、Javascript 逻辑、数据模型相分离的操作。Model和View是可以直接打交道的,这种架构下,其实还是处在 “无框架时代”之前的状态管理,存在大量的DOM 操作使页面渲染性能降低,加载速度变慢,影响用户体验。
*

在客户端MVC应用程序中,用户交互会触发控制器Controller中的代码。控制器Controller知道如何通过调用模型上的方法来协调对一个或多个模型Model的更改。当模型Model更改时,它们会通知一个或多个视图View,这些视图View又从模型Model中读取新数据并进行相应的更新,以便用户可以看到该新数据。

在这里插入图片描述

2、MVVM架构(Model-View-ViewModel 模型-视图-视图模型)
本质上就是MVC 的改进版,通过数据来驱动视图层的显示而不是节点操作
,将mvc 中 Controller演变成 mvvm 中的 viewModel,ViewModel来同步视图和数据绑定。本质上也是经常说的双向绑定,例如:Vue、AngularJs在这里插入图片描述
PS:React 本质上不属于MVVM 架构,也不属于MVC,React的核心思想是声明式渲染(通过状态去形容最后的网页长什么样子)、组件化(尽可能的将页面拆分成一个个可以复用的组件)、单向数据流(数据主要从父节点通过props传递到子节点,如果顶层某个props改变了,React会重新渲染所有的子节点)。

二、Flux

1、react、vue、angularjs 等框架都提供了自己的状态管理机制,如React的本地状态或Vue的响应式数据。这些适用于简单的应用程序或组件级别的状态管理,但对于大型应用程序可能会变得复杂和难以维护,比如:组件之间的状态共享等。

2、Facebook 针对现有前端MVC框架的局限总结出来的一套基于dispatcher的前端应用架构模式。
在这里插入图片描述
Flux的核心思想就是数据和逻辑永远单向流动。数据从action到dispatcher,再到store,最终到view的路线是单向不可逆的,各个角色之间不会像前端MVC模式那样复杂的依赖。

3、Flux特点:

  • 单向数据流。视图事件或者外部测试用例发出 Action ,经由 Dispatcher 派发给 Store ,Store 会触发相应的方法更新数据、更新视图。
  • Store 可以有多个。Store 不仅存放数据,还封装了处理数据的方法。

4、Flux基本组成:

  • dispatcher:负责分发事件
  • store:负责保存数据,同时响应事件并更新数据
  • view:负责订阅store中的数据,并使用这些数据渲染相应的页面
  • action:触发dispatcher的对象

Flux 重要的不是这个框架,而是他提出的思想,类似MVC/MVVM之类的,在Flux的启发下,出现了各种的状态管理框架。

  • 单向数据流、中心化管理、函数式:redux、zustand、dva等
  • 响应式、集中式管理:mobx、valtio、vuex等
  • 原子状态模式、分散管理:recoil、jotai等

三、单向数据流、中心化管理、函数类型的状态管理容器

Redux

Redux是Flux的一个优秀实现,但在实现上与Flux有所不同,是JavaScript应用可预测的状态管理容器。
在这里插入图片描述

1、特点:

  • 单向数据流。View 发出 Action (store.dispatch(action)),Store 调用 Reducer 计算出新的 state ,若 state 产生变化,则调用监听函数重新渲染 View (store.subscribe(render))。
  • 单一数据源。整个应用的State储存在单个Store的对象树中。
  • 数据不可变,state状态是只读的,不能直接修改,只能通过reducers纯函数来进行处理state,每次都返回一个新的状态对象。

2、核心概念

  • Store(存储):Redux使用一个单一存储来保存应用程序的状态。存储是一个包含状态和操作的对象。你可以通过调用createStore函数来创建Redux存储。

  • State(状态):状态是存储中的数据,代表了应用程序的当前状态。状态是只读的,不能直接修改,只能通过发起操作来改变。

  • Action(操作):操作是一个描述状态变化的普通JavaScript对象。它必须包含一个type字段来表示操作的类型,可以包含其他自定义字段来传递数据。你可以通过调用store.dispatch(action)来发起一个操作。

  • Reducer(归约器):归约器是一个纯函数,它接收当前状态和操作,然后返回一个新的状态。归约器定义了如何根据操作来更新状态。你可以通过调用store.getState()来获取当前状态。

  • Dispatch(分发):分发是将操作发送到存储的过程。你可以通过调用store.dispatch(action)来发起一个操作。

  • Subscribe(订阅):订阅是用于监听状态变化的过程。你可以通过调用store.subscribe(listener)来注册一个监听器函数,当状态发生变化时,监听器函数将被调用。

  • Middleware(中间件):中间件是位于操作发起和归约器之间的一层拦截器。它可以捕获和处理操作,以实现一些额外的功能,如异步操作、日志记录、错误处理等。使用applyMiddleware函数将它们应用到Redux存储中,可以加载多个中间件,中间件顺序执行。常用中间件有:

    • Redux Thunk:用于处理异步操作,支持在操作中返回函数而不仅仅是普通的对象。
    • Redux Saga:使用Generator函数来处理复杂的异步流程,提供了强大的控制流程和错误处理能力。
    • Redux Logger:用于记录操作和状态变化的中间件,方便调试和追踪应用程序的状态。
    • Redux Promise:用于处理返回Promise的操作,可以简化异步操作的处理流程。

下面是一个简单的使用Redux的示例:

import { createStore ,applyMiddleware} from 'redux';
import thunk from 'redux-thunk';
import saga from 'redux-saga';

// 定义初始状态
const initialState = {
count: 0,
};

// 定义操作类型
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

// 定义归约器
const reducer = (state = initialState, action) => {
switch (action.type) {
  case INCREMENT:
    return { ...state, count: state.count + 1 };
  case DECREMENT:
    return { ...state, count: state.count - 1 };
  default:
    return state;
}
};

// 创建Redux存储,添加中间件
const store = createStore(
  reducer,
  applyMiddleware(thunk,saga)
);

// 订阅状态变化
store.subscribe(() => {
    const state = store.getState();
    console.log('Current count:', state.count);
});

// 发起操作
store.dispatch({ type: INCREMENT });
store.dispatch({ type: INCREMENT });
store.dispatch({ type: DECREMENT });

3、Redux 并不是 React 的专利,React 可以用,Vue 可以用,原生 JavaScript 也可以用。但是,因为Redux 和 React 几乎是同时出来的,React的数据流管理方案大多数都使用了Redux(需要借助React-redux,React 的官方 Redux UI 绑定库)。

4、缺点:

  • 不易学习掌握,概念较多。
  • 冗余的模板代码,导致代码量增加,维护繁重
  • 复杂性增加,过度的使用会导致复杂性增加,理解困难

dva

Dva是在Redux基础上进行封装和扩展的一个前端框架,旨在简化React应用程序的开发流程和提高开发效率。它提供了一套约定和规范,使得状态管理、异步操作和路由管理更加容易上手和理解。在React开发中,希望在使用Redux的同时提高开发效率,那么Dva可能是一个不错的选择。

  • 学习曲线:相对于纯Redux来说,Dva的学习曲线可能较为平缓。Dva封装了Redux和Redux-Saga,并提供了一套约定和规范,使得开发者可以更快地上手和理解。它提供了一种基于模型的开发方式,将状态、操作和视图组件集成在一起,简化了开发流程。

  • 代码结构:在使用纯Redux时,你需要手动创建和管理存储、归约器、操作和中间件等。而Dva使用了一种约定的代码结构,将这些概念封装在一起,通过一个模型文件来定义状态和操作,从而减少了样板代码的编写,并提供了更清晰的代码组织方式。

  • 异步操作:在Redux中处理异步操作需要使用中间件,如Redux Thunk或Redux Saga。而Dva内置了Redux-Saga,使得处理异步操作变得更加简单和直观。你可以在Dva的模型中定义effect函数来处理异步逻辑,而无需手动编写Redux-Saga的代码。

  • 路由管理:Redux本身并不提供路由管理功能,而Dva集成了React-Router,提供了方便的路由配置和管理。你可以在Dva的路由配置中定义页面和对应的模型,使得状态管理和路由管理更加一体化。

  • 插件生态系统:Dva拥有丰富的插件生态系统,提供了许多常用的功能插件,如dva-loading、dva-immer等。这些插件可以进一步简化开发流程,并提供额外的功能和增强。

下面是一个dva使用的简单示例:

import React from 'react';
import { createBrowserHistory } from 'history';
import { Router, Route, Switch } from 'dva/router';
import { create } from 'dva-core';
import { Provider } from 'react-redux';

// 创建Dva实例
const app = create();

// 注册模型
app.model({
  namespace: 'counter',
  state: 0,
  reducers: {
    increment(state) {
      return state + 1;
    },
    decrement(state) {
      return state - 1;
    },
  },
  effects: {
    *asyncIncrement(action, { call, put }) {
      yield call(delay, 1000); // 模拟异步操作
      yield put({ type: 'increment' });
    },
  },
});

// 启动Dva
app.start();

// 延时函数
function delay(timeout) {
  return new Promise(resolve => {
    setTimeout(resolve, timeout);
  });
}

// 创建根组件
function App() {
  return (
    <div>
      <h1>Dva Demo</h1>
      <Counter />
    </div>
  );
}

// 创建计数器组件
function Counter() {
  const { dispatch, counter } = app._store.getState().counter;

  const handleIncrement = () => {
    dispatch({ type: 'counter/increment' });
  };

  const handleDecrement = () => {
    dispatch({ type: 'counter/decrement' });
  };

  const handleAsyncIncrement = () => {
    dispatch({ type: 'counter/asyncIncrement' });
  };

  return (
    <div>
      <p>Counter: {counter}</p>
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
      <button onClick={handleAsyncIncrement}>Async Increment</button>
    </div>
  );
}

// 渲染应用程序
const history = createBrowserHistory();
const rootElement = document.getElementById('root');
ReactDOM.render(
  <Provider store={app._store}>
    <Router history={history}>
      <Switch>
        <Route path="/" component={App} />
      </Switch>
    </Router>
  </Provider>,
  rootElement
);

在上面的示例中,我们创建了一个简单的计数器应用程序。Dva的app.model函数用于注册一个模型,包含了命名空间、初始状态、reducers和effects。我们定义了两个reducers用于增加和减少计数器的值,以及一个effect用于模拟异步操作。handleIncrement、handleDecrement和handleAsyncIncrement函数分别用于触发对应的action。

在根组件App中,我们渲染了一个Counter组件,用于展示计数器的值和操作按钮。通过app._store.getState().counter可以获取到counter模型的状态和dispatch函数。

最后,我们使用ReactDOM.render将应用程序渲染到页面上,并使用Provider组件将Dva的状态管理能力注入到React组件中。

zustand

Zustand是一个简单、轻量级的状态管理库,用于React应用程序。它提供了一种基于钩子函数的方式来管理和更新应用程序的状态,同时保持了简洁性和灵活性。

  • 简单易用:Zustand的API设计简洁明了,使用起来非常简单。它采用了类似于React的钩子函数的语法,使得状态管理变得直观和易于理解。开发者可以通过调用createStore函数创建一个状态容器,然后使用useStore钩子函数在组件中访问和更新状态。

  • 轻量高效:Zustand的体积非常小,而且具有出色的性能。它使用了高性能的Proxy代理机制来实现状态的访问和更新,避免了不必要的重新渲染,从而提高了应用程序的性能。

  • 状态共享和组合:Zustand支持状态的共享和组合。你可以在应用程序的不同组件中使用同一个状态容器,从而实现状态的共享和统一管理。此外,你还可以使用combine函数将多个状态容器组合成一个更大的状态容器,使得状态的管理更加灵活和可扩展。

  • 中间件支持:Zustand提供了中间件机制,允许你在状态更新之前或之后执行自定义的逻辑。你可以使用中间件来处理异步操作、记录状态变更、实现状态持久化等。

  • TypeScript支持:Zustand完全支持TypeScript,并提供了类型推断和类型安全的状态访问。这使得在使用Zustand时能够获得更好的开发体验和更高的代码可靠性。

Zustand是一个相对简单的状态管理库,适用于中小型的React应用程序。如果应用程序非常复杂或需要更高级的功能(如状态持久化等),可能需要考虑使用更完整的状态管理解决方案,如Redux或MobX。

下面是一个使用Zustand的示例:

import React from 'react';
import create from 'zustand';
import { Provider, useStore } from 'zustand/react';

// 创建状态管理器
const useCounterStore = create(set => ({
  counter: 0,
  increment: () => set(state => ({ counter: state.counter + 1 })),
  decrement: () => set(state => ({ counter: state.counter - 1 })),
}));

// 创建根组件
function App() {
  return (
    <div>
      <h1>Zustand Demo</h1>
      <Counter />
    </div>
  );
}

// 创建计数器组件
function Counter() {
  const { counter, increment, decrement } = useStore(useCounterStore);

  const handleIncrement = () => {
    increment();
  };

  const handleDecrement = () => {
    decrement();
  };

  return (
    <div>
      <p>Counter: {counter}</p>
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
    </div>
  );
}

// 渲染应用程序
const rootElement = document.getElementById('root');
ReactDOM.render(
  <Provider {...useCounterStore}>
    <App />
  </Provider>,
  rootElement
);

在上面的示例中,我们使用create函数创建了一个状态管理器useCounterStore。该状态管理器包含了一个counter状态和两个更新状态的函数increment和decrement。

在Counter组件中,我们使用useStore钩子从状态管理器中获取counter、increment和decrement。通过调用对应的函数,我们可以更新状态并重新渲染组件。

最后,我们使用ReactDOM.render将应用程序渲染到页面上,并使用Provider组件将状态管理器注入到组件中。

四、响应式、集中式管理容器

mobx

MobX是一个用于状态管理的JavaScript库,它提供了一种简单而强大的方式来管理应用程序中的可变状态。它的设计目标是使状态管理变得简单、可扩展和高效。

MobX基于观察者模式,通过将状态转换为可观察的对象,当状态发生变化时,自动通知相关的观察者进行更新。这种自动化的响应机制使得开发者无需手动编写繁琐的状态更新逻辑,而是可以专注于定义状态和处理业务逻辑。

  • 可观察对象(Observable):将普通的JavaScript对象、数组或类转换为可观察的对象,使其能够被观察和跟踪变化。

  • 观察者(Observer):通过使用observer函数或装饰器,将组件或函数转换为观察者,使其能够自动响应可观察对象的变化并更新界面。

  • 动作(Action):用于修改可观察对象的方法,通过使用@action装饰器或action函数来标记。动作确保状态的修改是在事务中进行的,从而保持状态的一致性。

  • 计算属性(Computed):基于可观察对象的派生属性,当它所依赖的可观察对象发生变化时,自动重新计算。计算属性可以提供便利的数据转换和衍生逻辑。

下面是一个使用MobX的简单示例:

import React from 'react';
import { observer } from 'mobx-react';
import { observable, action } from 'mobx';

// 创建可观察的状态
class CounterStore {
  @observable counter = 0;

  @action increment() {
    this.counter++;
  }

  @action decrement() {
    this.counter--;
  }
}

// 创建根组件
const App = observer(() => {
  const counterStore = new CounterStore();

  const handleIncrement = () => {
    counterStore.increment();
  };

  const handleDecrement = () => {
    counterStore.decrement();
  };

  return (
    <div>
      <h1>MobX Demo</h1>
      <Counter counterStore={counterStore} />
      <button onClick={handleIncrement}>Increment</button>
      <button onClick={handleDecrement}>Decrement</button>
    </div>
  );
});

// 创建计数器组件
const Counter = observer(({ counterStore }) => {
  return <p>Counter: {counterStore.counter}</p>;
});

// 渲染应用程序
const rootElement = document.getElementById('root');
ReactDOM.render(<App />, rootElement);

在上面的示例中,我们创建了一个可观察的状态CounterStore,其中包含了一个counter属性和两个更新状态的动作increment和decrement。通过使用@observable和@action装饰器,我们将这些属性和动作转换为可观察的状态。

在根组件App中,我们创建了一个CounterStore实例,并将其作为属性传递给Counter组件和按钮的点击事件处理函数。通过observer函数包裹App和Counter组件,使其成为观察者,当状态发生变化时自动重新渲染。

valtio

Valtio是一个轻量级的状态管理库,专为React应用程序设计。它提供了一种简单而直观的方式来管理应用程序的状态,并与React的生命周期和渲染机制紧密集成。

Valtio的设计目标是使状态管理变得简单、可预测和高效。它采用了基于代理的观察机制,通过将状态对象包装在一个代理对象中,实现对状态变化的监听和触发。

  • 创建状态对象:使用proxy函数创建一个代理对象,将你的状态对象作为参数传递给它。代理对象会监听状态对象的变化。

  • 访问和修改状态:通过直接访问代理对象的属性来读取状态,通过赋值操作来修改状态。Valtio会自动捕获状态的变化,并触发相关的更新。

  • 订阅状态变化:使用subscribe函数来订阅状态的变化。当状态发生变化时,订阅者会被通知,并执行相应的回调函数。

  • 在React组件中使用状态:将状态对象传递给React组件,并在组件中使用useSnapshot钩子来获取状态的快照。这样,当状态发生变化时,组件会自动重新渲染。

下面是一个使用Valtio的简单示例:

import { proxy, useSnapshot } from 'valtio';
import React from 'react';

// 创建状态对象
const state = proxy({ count: 0 });

// 组件A
function ComponentA() {
  const snapshot = useSnapshot(state); // 获取状态的快照

  return (
    <div>
      <h2>Component A</h2>
      <p>Count: {snapshot.count}</p>
      <button onClick={() => state.count++}>Increment</button>
    </div>
  );
}

// 组件B
function ComponentB() {
  const snapshot = useSnapshot(state); // 获取状态的快照

  return (
    <div>
      <h2>Component B</h2>
      <p>Count: {snapshot.count}</p>
      <button onClick={() => state.count--}>Decrement</button>
    </div>
  );
}

// 应用程序
function App() {
  return (
    <div>
      <ComponentA />
      <ComponentB />
    </div>
  );
}

export default App;

在上面的示例中,我们首先使用proxy函数创建了一个状态对象state,其中包含一个名为count的属性。然后,我们定义了两个React组件ComponentA和ComponentB,它们分别显示和修改count属性的值。在组件中,我们使用useSnapshot钩子来获取状态的快照,并将其显示在界面上。通过点击按钮,我们可以增加或减少count的值。

最后,我们在App组件中渲染了ComponentA和ComponentB。当状态发生变化时,Valtio会自动触发相关组件的重新渲染,以反映最新的状态。

Vuex

Vuex是一个专为Vue.js应用程序开发的状态管理模式和库。它可以帮助开发者管理应用程序的共享状态,并提供一种可预测的方式来处理状态的变化。

在大型的Vue.js应用程序中,组件之间的状态共享和通信可能会变得复杂和混乱。Vuex通过引入一个集中式的状态存储,将应用程序的状态抽离到一个单独的地方,使得状态的变化和管理更加可控和可预测。

  • State(状态):应用程序的状态存储在一个单一的对象中,称为state。可以通过读取state的属性来获取状态的值。

  • Mutations(突变):Mutations是用于修改状态的函数。它们是同步的操作,每个Mutation都有一个字符串类型的事件名称和一个处理函数。通过提交一个Mutation来修改状态。

  • Actions(动作):Actions是用于处理异步操作和提交Mutations的函数。它们可以包含任意异步操作,如网络请求、定时器等。Actions通过提交一个Mutation来间接地修改状态。

  • Getters(获取器):Getters是用于从状态中派生出新的状态的函数。它们可以对state进行计算和过滤,并返回一个新的值。

  • Modules(模块):Modules允许将Vuex的状态树分割成多个模块。每个模块都有自己的state、mutations、actions和getters,可以嵌套和组合在一起,以便更好地组织和管理状态。

在你的项目中创建一个名为store.js的文件,并添加以下代码:

import Vue from 'vue';
import Vuex from 'vuex';

Vue.use(Vuex);

// 创建一个新的Vuex store
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment(state) {
      state.count++;
    },
    decrement(state) {
      state.count--;
    }
  },
  actions: {
    increment(context) {
      context.commit('increment');
    },
    decrement(context) {
      context.commit('decrement');
    }
  },
  getters: {
    getCount(state) {
      return state.count;
    }
  }
});

export default store;

在上面的代码中,我们首先导入Vue和Vuex,并使用Vue.use(Vuex)来安装Vuex插件。然后,我们创建了一个新的Vuex store,其中包含了state、mutations、actions和getters。

state:存储应用程序的状态,这里只有一个count属性。
mutations:定义了两个Mutation函数,increment和decrement,用于修改状态。
actions:定义了两个Action函数,increment和decrement,用于处理异步操作和提交Mutations。
getters:定义了一个Getter函数getCount,用于从状态中派生出新的状态。
接下来,在你的Vue组件中使用Vuex。例如,在你的组件中使用count状态和increment、decrement操作,可以按照以下方式进行:

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
    <button @click="decrement">Decrement</button>
  </div>
</template>

<script>
import { mapState, mapActions } from 'vuex';

export default {
  computed: {
    ...mapState(['count'])
  },
  methods: {
    ...mapActions(['increment', 'decrement'])
  }
};
</script>

在上面的代码中,我们使用mapState辅助函数将count状态映射到组件的计算属性中,以便在模板中使用。同时,我们使用mapActions辅助函数将increment和decrement操作映射到组件的方法中,以便在模板中调用。

最后,在你的Vue根实例中引入和使用Vuex store。例如,在你的main.js文件中:

import Vue from 'vue';
import App from './App.vue';
import store from './store';

new Vue({
  store,
  render: h => h(App)
}).$mount('#app');

在上面的代码中,我们导入了Vuex store,并将其作为store选项传递给Vue根实例。这样,整个应用程序都可以访问和使用Vuex store中定义的状态和操作。

五、原子状态模式、分散管理类型状态管理容器

recoil

Recoil是一个由Facebook开发的状态管理库,用于构建React应用程序。它旨在提供一种简单、可扩展且易于测试的方式来管理应用程序中的状态。

Recoil的设计目标是解决React应用程序中状态管理的复杂性和灵活性问题。它提供了一组用于定义、访问和更新状态的API,以及一些高级功能来处理状态之间的依赖关系和副作用。

  • Atoms(原子):Atoms是状态的基本单位。它们类似于React组件中的状态变量,但可以在整个应用程序中共享和访问。Atoms可以存储任意类型的数据,并且可以通过Recoil的API来读取和更新。

  • Selectors(选择器):Selectors用于派生出新的状态值。它们可以从一个或多个Atoms中获取数据,并对其进行转换、过滤和组合,生成一个新的派生状态。Selectors可以作为其他Atoms或Selectors的依赖,并在它们发生变化时自动更新。

  • Effects(副作用):Effects用于处理副作用,例如异步数据获取、网络请求等。通过使用Recoil提供的异步API,可以在Effects中进行异步操作,并将结果更新到Atoms中。

  • Hooks(钩子):Recoil提供了一组React钩子函数,用于在组件中访问和更新状态。这些钩子函数包括useRecoilState、useRecoilValue、useSetRecoilState等,使得状态管理在React组件中变得简单和直观。

在你的项目中创建一个名为counterState.js的文件,并添加以下代码:

import { atom } from 'recoil';

export const counterState = atom({
  key: 'counter',
  default: 0
});

在上面的代码中,我们使用atom函数创建了一个名为counterState的Recoil状态。key属性用于唯一标识状态,default属性指定了状态的初始值。

接下来,在你的React组件中使用Recoil状态。例如,在你的组件中使用counterState状态和set函数,可以按照以下方式进行:

import React from 'react';
import { useRecoilState } from 'recoil';
import { counterState } from './counterState';

function Counter() {
  const [counter, setCounter] = useRecoilState(counterState);

  const increment = () => {
    setCounter(counter + 1);
  };

  const decrement = () => {
    setCounter(counter - 1);
  };

  return (
    <div>
      <p>Count: {counter}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}

export default Counter;

在上面的代码中,我们使用useRecoilState钩子函数来访问和更新counterState状态。通过解构赋值,我们可以获取counter状态和setCounter函数,用于更新状态。

在组件中,我们定义了increment和decrement函数,分别用于增加和减少计数器的值。通过调用setCounter函数并传递新的值,我们可以更新counterState状态。

最后,在你的应用程序中使用Counter组件。例如,在你的根组件中:

import React from 'react';
import { RecoilRoot } from 'recoil';
import Counter from './Counter';

function App() {
  return (
    <RecoilRoot>
      <Counter />
    </RecoilRoot>
  );
}

export default App;

在上面的代码中,我们使用RecoilRoot组件将Recoil状态提供给整个应用程序。在RecoilRoot组件内部,我们渲染了Counter组件,使其可以访问和使用Recoil状态。

jotai

Jotai 用于构建 React 应用程序。它提供了一种简单且可扩展的方式来管理应用程序中的状态。他的设计灵感来自于 Recoil,但它采用了一种更加简化和直观的 API。Jotai 的核心概念是 atoms(原子)和 derived atoms(派生原子)。

  • Atoms(原子):Atoms 是状态的基本单位,类似于 Recoil 中的 atoms。你可以使用 atom 函数来定义一个 atom,并指定初始值。与 Recoil 不同的是,Jotai 的 atoms 是可变的,你可以直接修改它们的值。

  • Derived Atoms(派生原子):Derived atoms 是从一个或多个 atoms 派生出来的状态。你可以使用 atom 函数的第一个参数来定义 derived atom,并使用一个回调函数来计算派生状态的值。当依赖的 atoms 发生变化时,derived atom 会自动重新计算。

Jotai 还提供了一些钩子函数,用于在 React 组件中访问和更新状态。这些钩子函数包括 useAtom、useAtomValue、useSetAtom 等。

下面是一个简单的使用 Jotai 的示例::
在你的项目中创建一个名为 counterAtom.js 的文件,并添加以下代码:

import { atom } from 'jotai';

export const counterAtom = atom(0);

在上面的代码中,我们使用 atom 函数创建了一个名为 counterAtom 的 Jotai 原子,并指定了初始值为 0。

然后,在你的 React 组件中使用 Jotai 状态。例如,在你的组件中使用 counterAtom 状态,可以按照以下方式进行:

import React from 'react';
import { useAtom } from 'jotai';
import { counterAtom } from './counterAtom';

function Counter() {
  const [counter, setCounter] = useAtom(counterAtom);

  const increment = () => {
    setCounter((prevCounter) => prevCounter + 1);
  };

  const decrement = () => {
    setCounter((prevCounter) => prevCounter - 1);
  };

  return (
    <div>
      <p>Count: {counter}</p>
      <button onClick={increment}>Increment</button>
      <button onClick={decrement}>Decrement</button>
    </div>
  );
}

export default Counter;

在上面的代码中,我们使用 useAtom 钩子函数来访问和更新 counterAtom 状态。通过解构赋值,我们可以获取 counter 状态和 setCounter 函数,用于更新状态。

在组件中,我们定义了 increment 和 decrement 函数,分别用于增加和减少计数器的值。通过调用 setCounter 函数并传递一个更新函数,我们可以更新 counterAtom 状态。

最后,在你的应用程序中使用 Counter 组件。例如,在你的根组件中:

import React from 'react';
import { Provider } from 'jotai';
import Counter from './Counter';

function App() {
  return (
    <Provider>
      <Counter />
    </Provider>
  );
}

export default App;

在上面的代码中,我们使用 Provider 组件将 Jotai 状态提供给整个应用程序。在 Provider 组件内部,我们渲染了 Counter 组件,使其可以访问和使用 Jotai 状态。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值