本文主要是通过hook中的useContext、useReducer这二API去现实一个自己redux状态管理,而非去装插件实现状态管理。
出于学习的目的总结实现方式,至于能不能完全取代之前的状态管理(大型项目),本人还是处在观望状态,但不代表完全没用哦,小伙伴们。
好了废话跳过,下面开始实战:
首先用 create-react-app 创建react项目(git仓库),这里我使用react官方的脚手架,是方便你可以直接跟着实践
1 2 3 4 |
npx create-react-app my-app // 创建项目 // 创建好后进入my-app目录 yarn 或 npm i // 安装插件 |
创建基础useContext数据互通
useContext本身就是通过一层一层传递的,实现数据互通的。
1、创建一个models文件夹,并且创建MyProvider.js组件文件。
此组件类似redux中的Provider组件,挂载了store并且供子组件使用,只不过这里传递数据通过useContext实现
内容如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// MyProvider.js import React, { createContext } from "react"; // 创建 MyStoreContext,供子组件读取数据状态 export const MyStoreContext = createContext({}); /** * 创建一个 MyProvider 组件, * 通过此组件包裹后,所有子组件都可以通过调用 MyStoreContext 访问到 value 上的所有数据 */ export const MyProvider = props => { return ( <MyStoreContext.Provider value={{ color: "blue", age: 18 }}> {props.children} </MyStoreContext.Provider> ); }; |
2、创建一个展示读取共享数据的组件,创建components文件夹,并且创建ShowText.js文件
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// ShowText.js import React,{useContext} from 'react'; import {MyStoreContext} from '../models/MyProvider'; const ShowText = (props) => { const { color,age } = useContext(MyStoreContext); return ( <> <div style={{color}}>字体颜色展示</div> <div>年龄{age}</div> </> ) } export default ShowText; |
3、App.js根组件中,使用MyProvider包裹共享状态,在内的所有组件都是可以访问到MyProvider组件挂载的属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
import React from 'react'; import './App.css'; import ShowText from './components/ShowText'; import {MyProvider} from './models/MyProvider'; function App() { return ( <div className="App"> <MyProvider> <ShowText/> </MyProvider> </div> ); } export default App; |
此时子组件是可以看到文字颜色以及age属性,但现在还是死数据,下面开始使用useReducer 整合进来更新状态。
整合useReducer到useContext
1、创建module/reducer.js文件,管理如何更新全局共享的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// reducer.js export const UPDATE = 'UPDATE'; export const reducer = (state, action) => { switch (action.type) { case UPDATE: // 当为UPDATE事件时,更新state中的二个值,color和age return { ...state, color: action.color, age: action.age, }; default: return state; } }; |
2、改造MyProvider.js文件,把dispatch更新数据方法挂载在状态管理中,供子组件调用此方法更新页面状态数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
// MyProvider.js import React, { createContext,useReducer } from "react"; import {reducer} from './reducer'; // 引入更新数据状态逻辑 // 创建 context export const MyStoreContext = createContext({}); /** * 创建一个 MyProvider 组件 * MyProvider 组件包裹的所有子组件都可以通过调用 MyStoreContext 访问到 value */ export const MyProvider = props => { // 定义了初始数据状态 color:blue, age:18 const [store, dispatch] = useReducer(reducer, { color: 'blue', age: 18, }) return ( // 把dispatch方法也挂载在value中,供直接调用更新数据使用 <MyStoreContext.Provider value={{ ...store, dispatch }}> {props.children} </MyStoreContext.Provider> ); }; |
3、此时我们在建立一个子组件来触发更新数据,检测是否另外一个子组件能否更新数据及渲染到页面上。
接着创建 components/ButtonDemo.js组件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
import React,{useContext} from "react"; import {MyStoreContext} from '../models/MyProvider'; // 引入共享的store import {UPDATE} from '../models/reducer'; // 引入type const ButtonDemo = props => { const { dispatch } = useContext(MyStoreContext); return ( <> <button onClick={() => { // 传入更新数据 dispatch({ type: UPDATE, color: "red", age: 66 }); }} > 红色 </button> <button onClick={() => { dispatch({ type: UPDATE, color: "yellow", age: 28 }); }} > 黄色 </button> </> ); }; export default ButtonDemo; |
4、接着把ButtonDemo组件在App.js中引入进来,然后就可以测试数据状态管理能否及时更新了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
import ButtonDemo from './components/ButtonDemo'; import ShowText from './components/ShowText'; // .. function App() { return ( <div className="App"> <MyProvider> <ButtonDemo/> <ShowText/> </MyProvider> </div> ); } export default App; |
至此使用hook的API去实现redux方法结束。
总结思考:
之前开篇前就说不太合适大型项目,这是什么呢?在实践过程中发现当状态更改时,整体的dom tree都会被更新,那么这时候就会引发一个情况,全部节点触发更新渲染,那对于大一点的项目来说,渲染性能消耗还是比较大的。
主要是context逐层传递会让每个props触发更新机制,至于每个页面每个组件去监听props处理这块就。。。。。你懂的。
期望未来react版本中有所改进这块。