React中的状态管理是开发人员需要解决的问题。 总有一些新库给你选择,而选择合适的库可能是一项困难的工作

状态管理一直是React中开发人员需要解决的问题,如何有条理的组织数据,如何快速的在项目中集成,这些都是我们做项目时选择技术的标准。

Redux一直是我们react项目中不二的状态管理插件,但是redux的配置以及各种插件的安装一直是很多人员头疼的一个问题,太麻烦了。但是随着ReduxToolkit的出现,确实解决了这个问题,直接安装,就不再需要继续安装各类其他插件,直接上手就能用,简单方便。但是很多时候,我们的项目可能根本不需要这么笨重的插件,虽然redux很好,但是毕竟这么多年过去了,一代新人换旧人。前端这个大坑中,总会出现新的技术、框架来埋葬那些老家伙。

代理
Proxy对象用于创建一个对象的代理,从而实现基本操作的拦截和自定义(如属性查找、赋值、枚举、函数调用等)。

自ES6版本以来,我们在JavaScript中有代理。 代理接收两个参数:

target - 要代理的原始对象
handler - 定义对象的操作
这是我们如何使用JavaScript创建代理的方式:

const handler = {
get: function (obj, prop) {
return prop in obj ? obj[prop] : 37;
},
};

const p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;

console.log(p.a, p.b); // 1, undefined
console.log('c' in p, p.c); // false, 37

Valtio
Valtio是一个用于对React和JavaScript应用程序简单的代理状态的库。它使用了js中Proxy这个语法,能让我们可以非常方便的在react中集成状态管理功能。

npm init vite@latest # 创建react项目
npm i valtio # 进入项目,安装依赖

我们需要做的是包装我们的状态对象,然后我们可以在我们的应用程序中的任何地方进行改变:

import { proxy } from 'valtio';

type IStore = {
count: number;
list: Item[];
};

// 定义数据,使用proxy进行包括修饰
const store = proxy<IStore>({
count: 1,
list: [],
});

/**
* 改变数据
* @param step
*/
export const countPlus = (step: number) => {
store.count += step;
};

/**
* 新增数据到列表
* @param txt
*/
export const addToList = (txt: string) => {
store.list.push({
id: Date.now(),
txt,
});
};

export default store;

定义好的数据直接在组件中进行引入就能使用,useSnapshot可以获取我们定义好的状态数据,使用之后在组件中就是一个响应式的效果,只要数据改变了组件就会直接进行更新

import { useSnapshot } from 'valtio';
import store, { countPlus } from '../store';

function Counter() {
const { count } = useSnapshot(store);
return (
<div>
<h1>计数器--{count}</h1>
<button
onClick={() => {
countPlus(2);
}}
>
改变
</button>
</div>
);
}

export default Counter;

valtio简单直接,充分使用了proxy这个对象,简单粗暴的实现react项目的状态管理。在一些小型项目中是一个不错的选择。当然当然大家还是需要在项目中努力做好自己代码结构的组织方便后去数据的维护。

├── index.html
├── package-lock.json
├── package.json
├── public
│ └── vite.svg
├── readme.md
├── src
│ ├── @types
│ │ └── app.d.ts
│ ├── App.css
│ ├── App.tsx
│ ├── assets
│ │ └── react.svg
│ ├── components
│ │ ├── Counter.tsx
│ │ ├── List
│ │ │ ├── index.tsx
│ │ │ └── list-input.tsx
│ │ ├── Movies.tsx
│ │ └── ShowCounter.tsx
│ ├── index.css
│ ├── main.tsx
│ ├── store
│ │ ├── features
│ │ │ └── movie.ts
│ │ └── index.ts
│ └── vite-env.d.ts
├── tsconfig.json
├── tsconfig.node.json
└── vite.config.ts

store,目录存储valtio的数据。index.ts做为入口,可以引入其他的子节点数据

store/features,目录可以根据功能做拆分处理。每一个功能对应一个文件

比如此处我的movie.ts文件的内容如下

// store/features/movie.ts
import { proxy } from 'valtio';
// 进行分割
export const movie = proxy({
movies: [],
});

export const loadMovieData = async () => {
const res = await fetch(
'https://pcw-api.iqiyi.com/search/recommend/list?channel_id=2&data_type=1&mode=11&page_id=2&ret_num=48&session=ffad98ae609f650afe4b60e205948ac1&three_category_id=15;must'
).then((data) => data.json());
movie.movies = res.data.list;
};

完成的store/index.ts文件如下

import { proxy } from 'valtio';
import { movie } from './features/movie';

/**
* 定义数据
*/
const store = proxy<IStore>({
count: 1,
list: [],
movie, // 引入拆分好的文件
});

/**
* 改变数据
* @param step
*/
export const countPlus = (step: number) => {
store.count += step;
};

/**
* 新增数据到列表
* @param txt
*/
export const addToList = (txt: string) => {
store.list.push({
id: Date.now(),
txt,
});
};

export default store;

而我组件中如果要获取数据,可以直接拿来用

复制
// components/Movie.tsx
import { useEffect } from 'react';
import { useSnapshot } from 'valtio';
import store from '../store';
import { loadMovieData } from '../store/features/movie';

function Movies() {
const { movie } = useSnapshot(store); // useSnapshot取数据
// console.log(movie);
useEffect(() => {
loadMovieData();
}, []);
return (
<div className='movies'>
<ul>
{movie.movies.map((item: any) => (
<li key={item.albumId}>{item.name}</li>
))}
</ul>
</div>
);
}

export default Movies;