context
React 内置的 context 可以作为 store;全局管理组件之间的交互状态:
组件: /component/A.js
import React, { useContext } from 'react'
import { Context } from '../../store'
const A = () => {
const { count } = useContext(Context)
return (
<div className="a-component">
<p>A COMPONENT</p>
<p>{`count: ${count}`}</p>
</div>
)
}
export default A
组件: /component/B.js
import React, { useContext } from 'react'
import { Button } from 'antd-mobile'
import { Context } from '../../store'
const B = () => {
const { count, setCount, onChangeCount } = useContext(Context)
const handleAdd = () => {
setCount(count + 1)
}
const handleSub = () => {
setCount(count - 1)
}
const handleChange = () => {
onChangeCount()
}
return (
<div className="b-component">
<p>B COMPONENT</p>
<Button type="primary" size="small" onClick={handleAdd}>COUNT + 1</Button>
<Button type="ghost" size="small" onClick={handleSub}>COUNT - 1</Button>
<Button type="warning" size="small" onClick={handleChange}>COUNT CHANGE</Button>
</div>
)
}
export default B
状态管理:/store.js
import React, { createContext, useState } from 'react'
import { Toast } from 'antd-mobile'
const Context = createContext({})
const Provider = ({ children }) => {
const [count, setCount] = useState(0)
// 添加方法,模拟请求
const onChangeCount = () => {
// 模拟请求,两秒后修改值
Toast.loading('等待请求成功...', 20)
setTimeout(() => {
Toast.success('请求成功', 2)
setCount(9999)
}, 2000)
}
const VALUE = {
count,
setCount,
onChangeCount,
}
return (
<Context.Provider value={VALUE}>
{children}
</Context.Provider>
)
}
export {
Context,
Provider,
}
入口文件:/index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from './store'
import A from './component/A'
import B from './component/B'
const Index = () => (
<div className="index-wrap">
<A />
<B />
</div>
)
render(
<Provider>
<Index />
</Provider>,
document.getElementById('root'),
)
但是 context 做为 store 有一个问题,任何组件都能从 context 中取出数据来修改;
那么当排查问题的时候就特别困难,因为并不知道是哪个组件把数据改坏的,也就是数据流不清晰。
因此,此方式适用于业务不是很复杂的项目管理通用的状态。
useContext + useReducer
useContext + useReducer 完全可以替代 React 进行状态管理:
组件: /component/A.js
import React from 'react'
import { useStore } from '../../store'
const A = () => {
const {
store: { count },
} = useStore()
return (
<div className="a-component">
<p>A COMPONENT</p>
<p>{`count: ${count}`}</p>
</div>
)
}
export default A
组件: /component/B.js
import React from 'react'
import { Button, Toast } from 'antd-mobile'
import { useStore } from '../../store'
const B = () => {
const { dispatch } = useStore()
const handleAdd = () => {
dispatch({ type: 'add' })
}
const handleSub = () => {
dispatch({ type: 'sub' })
}
// 模拟请求,两秒后修改值
const handleChange = () => {
Toast.loading('等待请求成功...', 20)
setTimeout(() => {
Toast.success('请求成功', 2)
dispatch({ type: 'change', data: Math.ceil(Math.random() * 1000) })
}, 2000)
}
return (
<div className="b-component">
<p>B COMPONENT</p>
<Button type="primary" size="small" onClick={handleAdd}>COUNT + 1</Button>
<Button type="ghost" size="small" onClick={handleSub}>COUNT - 1</Button>
<Button type="warning" size="small" onClick={handleChange}>COUNT CHANGE</Button>
</div>
)
}
export default B
状态管理:/store.js
import React, { createContext, useContext, useReducer } from 'react'
const Context = createContext({})
const pageData = {
count: 0,
}
const reducer = (state, action) => {
const { type, data } = action
const { count } = state
if (type === 'add') {
return Object.assign({}, state, { count: count + 1 })
}
if (type === 'sub') {
return Object.assign({}, state, { count: count - 1 })
}
if (type === 'change') {
return Object.assign({}, state, { count: data })
}
return state
}
const Provider = ({ children }) => {
const [store, dispatch] = useReducer(reducer, pageData)
return (
<Context.Provider
value={{ store, dispatch }}
>
{children}
</Context.Provider>
)
}
const useStore = () => {
const { store, dispatch } = useContext(Context)
return { store, dispatch }
}
export {
Provider,
useStore,
}
入口文件:/index.js
import React from 'react'
import { render } from 'react-dom'
import { Provider } from './store'
import A from './component/A'
import B from './component/B'
const Index = () => (
<div className="index-wrap">
<A />
<B />
</div>
)
render(
<Provider>
<Index />
</Provider>,
document.getElementById('root'),
)
useContext 创建全局状态,不用一层一层的传递状态。
useReducer 创建 reducer 根据不同的 dispatch 更新 state。
代码写到哪里状态就加到哪里,不用打断思路跳到 redux 里面去写。
全局状态分离,避免项目变大导致 Redux 状态树难以管理。
文件目录
├── index.js // 入口文件
├── store.js // 状态管理
├── component
├── A.js // 组件A
└── B.js // 组件B