React-Redux、Recoil、useContext 做个简单的对比
作者:互联网
关注公众号: 微信搜索 前端工具人
; 收货更多的干货
文章来源: 自己掘金文章 https://juejin.cn/post/7073035305430810637/
React
生态丰富衍生了许多 状态管理
轮子;
开发时如何选择状态管理,也有些犯难;
在这把我所理解的、项目中用到过的(React-Redux, Recoil, useContext
)做个简单的对比,仅供参考;
一、个人建议
React-Redux
- 可以熟悉
React-Redux
的架构组件思想, 我觉得对日常开发很有用,Redux
重度患者的首选 - 共享状态多且庞大个人觉得可以优先考虑,结合
combineReducers
Recoil
- 日常开发完全够用,使用简单,容易上手,常用
API
也不是很多..., 挺香的 - 状态渲染导致组件重新渲染这块,
Recoil
有做优化, 性能还是很 Ok 的;
useContext
- 函数组件开发作为局部共享状态我觉得首选吧; 对于当前页面的共享状态,也交给全局状态管理,这肯定不符合
自己日常开发
- 全局状态管理
React-Redux
或者Recoil
(优先选择) - 局部状态
useContext
, 二者相结合;
当然还有许多优秀的状态管理插件, MobX
、hox
等等,自己没怎么用就不多做讨论了。
以下介绍有不对的,或者有更好的方法,请留言,先谢谢大佬
因为并不是一直在用 react
, 日常开发用的比较杂(vue3,react、flutter、electron
都用)、所以见解不是很深,只是把日常开发贴出来,仅供参考
二、React-Redux
个人觉得目前来说 React-Redux
相对于其他轮子,优势没那么明显了;
由于自己是从 Redux
过来的,前期轮子并不是很多选择,所以旧项目一直使用着 React-Redux
, 重构时也没替换;
缺点:
- 语法不够简洁,特别是没出 hook Api 的时候;
- 组件间的状态共享只能通过将 state 提升至它们的公共祖先来实现,但这样做可能导致重新渲染一颗巨大的组件树
优点:
React-Redux
架构组件思想我觉得非常的好, 很适用开发时对组件的一个拆分的粒度;
React-Redux 将所有组件分成两大类
- UI 组件(
presentational component
)
- 只负责
UI
的呈现,不带有任何业务逻辑- 没有状态(即不使用
state
这个变量)- 所有数据都由参数(
props
)提供- 不使用任何
Redux
的API
- 又称
纯组件
,即纯函数一样,纯粹由参数决定它的值, 没有任何副作用
- 容器组件(
container component
)
- 负责管理数据和业务逻辑,不负责
UI
的呈现- 带有内部状态, 可使用
Redux
的API
- 包含输入逻辑:外部的数据(即
state
对象)如何转换为UI
组件的参数- 输出逻辑:用户发出的动作如何变为
Action
对象,从UI
组件传出去
UI
组件负责 UI
的呈现,容器组件负责管理数据和逻辑
React-Redux
规定所有的 UI 组件都由用户提供,容器组件则由 React-Redux
自动生成。也就是说,用户负责视觉层,状态管理则是全部交给它
如果一个组件既有 UI
又有业务逻辑,那么将它拆分成:外面是一个容器组件,里面包了一个UI
组件。前者负责与外部的通信,将数据传给后者,由后者渲染出视图
代码片段
// store/index.js
// 以下类型涉及其他地方引入,就改为 any 了
import { createStore } from 'redux';
export const Types = {
CHANGE_DEVELOP: 'CHANGE_DEVELOP',
CHANGE_LANGUAGE: 'CHANGE_LANGUAGE'
}
const onChangeDevelop = <T = string>(val: T) => {
return {
type: Types.CHANGE_DEVELOP,
payload: val
}
}
const onChangeLanguage = <T = string>(val: T) => {
return {
type: Types.CHANGE_LANGUAGE,
payload: val
}
}
const baseState = {
language: 'zh',
developer: '前端工具人'
}
interface IAction {
type: string,
payload: any
}
const reducer = (state = baseState, action: IAction) => {
switch (action.type) {
case Types.CHANGE_LANGUAGE:
return { ...state, language: action.payload};
case Types.CHANGE_DEVELOP:
return { ...state, developer: action.payload };
default:
return state;
}
}
const configureStore = () => createStore(reducer)
export default configureStore;
// 项目入口 App.tsx
import { Provider } from 'react-redux'
import configureStore from './store/index'
const store = configureStore()
ReactDOM.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
document.getElementById('root')
);
// 使用 Home.tsx
import {useDispatch, useSelector} from 'react-redux'
const Header = () => {
const dispatch = useDispatch()
const developer = useSelector((state) => state.developer)
const onChangeDeveloper = ()=> { dispatch({ type: Types.CHANGE_DEVELOP, developer: '修改后的developer' }) }
}
三、Recoil
优势
我引用的是 Recoil
文档 的介绍, 吧啦吧啦一堆 详情直戳
-
React
官方提供的提供的状态管理库, 这官方2字就很nice
, 大胆的用吧; -
定义了一个有向图 (directed graph),正交同时又天然连结于你的
React
树上。状态的变化从该图的顶点(我们称之为atom
)开始,流经纯函数 (我们称之为selector
) 再传入组件 -
使用
Recoil
会为你创建一个数据流向图,从atom
(共享状态)到selector
(纯函数),再流向React
组件。Atom
是组件可以订阅的state
单位。selector
可以同步或异步改变此state
。 -
我们可以定义无需模板代码的
API
,共享的状态拥有与React
本地state
一样简单的get/set
接口 (当然如果需要,也可以使用reducer
等进行封装)。 -
我们有了与
Concurrent
模式及其他React
新特性兼容的可能性。 -
状态的定义是渐进式和分布式的,这使代码分割成为可能。
-
无需修改对应的组件,就能将它们本地的
state
用派生数据替换。 -
无需修改对应的组件,就能将派生数据在同步与异步间切换。
-
我们能将导航视为头等概念,甚至可以将状态的转变编码进链接中。
-
可以很轻松地以可回溯的方式持久化整个应用的状态,持久化的状态不会因为应用的改变而丢失
代码片段
// store/index.js
// 以下类型涉及其他地方引入,就改为 any 了
import React from 'react';
import { atom, selector, } from 'recoil';
// Recoil key 集合
const KEYS = {
COMMON_STATE: 'commonState'
PRODUCT_COUNT: 'productCount'
}
interface IBaseStore {
language: string,
developer: string,
email: string,
children: React.ReactNode[] | React.ReactNode
}
export const commonState = atom<IBaseStore>({
key: KEYS.COMMON_STATE,
default: {
language: 'zh',
developer: 'laisheng',
children: [],
email: '18826262167@163.com'
}
})
export const productCount = selector({
key: KEYS.PRODUCT_COUNT,
get: ({get}) => {
return get(commonState)
},
set:({set, reset, get}, newValue) => {
console.log(newValue)
}
})
// App.tsx
import { RecoilRoot } from 'recoil';
const App: React.FC<AppProps> = () => {
return (
<RecoilRoot>
<Router>
<RouterPage />
</Router>
</RecoilRoot>
)
}
export default App
// 使用 Home.tsx
import {useRecoilState, useRecoilValue} from 'recoil'
import { commonState } from '@/store'
const Header = () => {
const [comState, setComState] = useRecoilState(commonState);
setComState({ ...comState, developer: '更新工具人' })
}
四、useContext
这个作为 React-Hooks
自带的 buff
还是非常的ok
的
这个就没啥好介绍的了, 直接上代码片段吧
// Home.tsx
// 以下类型涉及其他地方引入,就改为 any 了
import React, { createContext, useMemo, useContext, useEffect, useReducer, useRef, useState, memo, useCallback } from 'react';
interface IHomeContext<T> {
state: T,
dispatch: any
}
interface IState {
communityId: string | number,
communityName: string,
macAddress: string | number,
doorControlName: string,
}
const homeState = {
communityId: '',
communityName: '',
macAddress: '',
doorControlName: ''
}
const HomeContext = createContext<IHomeContext<IState>>({
state: homeState,
dispatch: () => [],
})
// 类型这块这里就直接贴any了,因为要从其他地方导入类型
const onChangeHomeState = (state: any, data: any): IState => {
for (let key in data) {
// 避免 useContext 存储未定义的字段
if (key in state) state[key] = data[key]
}
return { ...state }
}
const Home: React.FC = () => {
const [state, dispatch] = useReducer(onChangeHomeState, homeState)
return (
<HomeContext.Provider value={{ state: useMemo(() => state, [state]), dispatch }}>
<div className="home-container">
<main>
<Header />
...
</main>
</div>
</HomeContext.Provider>
)
}
// 使用
const Header = () => {
const { state, dispatch } = useContext(HomeContext)
// age、 address onChangeHomeState 并不会处理, 更新的只有 communityId
dispatch({ 'communityId': 13, age: 18, address: '深圳' })
}
标签:状态,const,React,state,Recoil,组件,useContext,Redux 来源: https://www.cnblogs.com/ljx20180807/p/15986358.html