React状态管理——useState-useReducer+useContext实现全局状态管理
时间:2023-03-12 18:55:45
科技观察
useReducer是useState的替代品,用于处理复杂的状态或逻辑。当与其他Hooks(useContext)结合使用时,在不引入一些第三方状态管理库的情况下(例如Redux和Mobx)有时是一个不错的选择。目标在本文结束时,您将了解:使用ContextAPI。在哪些场景下可以使用Context代替Redux等第三方状态管理库。如何使用useState+useContext实现暗模式切换。如何使用useReducer+useContext实现待办事项。什么是语境?Context解决了跨组件之间的通信。也是官方默认的解决方案。不需要引入第三方库。适用于存储对组件树来说是全局的、不需要频繁更新数据的数据状态。例如:主题、当前经过身份验证的用户、首选语言。使用React.createContext方法创建上下文,该方法接受一个参数作为其默认值并返回MyContext.Provider、MyContext.ConsumerReact组件。constMyContext=React.createContext(defaultValue);MyContext.Provider组件接收传递给子组件(MyContext.Consumer使用的组件)的值属性,无论嵌套有多深。{children}将我们的内容包裹在MyContext.Consumer组件中,用于订阅上下文变化,一般写在类组件中。{value=>{value}}}上面引入的不必要的代码嵌套也增加了代码的复杂度。ReactHooks提供的useContext让访问Context状态变得更简单。constApp=()=>{constvalue=useContext(newContext);控制台日志(值);//thiswillreturn{color:'black'}return}上面我们做了一个Context,为了简单的理解,可以参考官网的Context和useContext的文档说明。下面我们通过两个例子来学习如何使用useContext来管理全局状态。useState+useContext主题切换本节中的第一个示例是使用Reacthooks的useState和useContextAPI的深色主题切换。实现上下文提供者在ThemeContext组件中,我们将主题定义为浅色和深色。定义ThemeProvider在context中维护两个属性:当前选择的主题theme,以及切换主题的函数toggleTheme()。ThemeProvider维护的这两个属性可以通过useContexthook在其他组件中获取。在使用useContext的时候,需要保证传入的是React.createContext创建的对象。这里我们可以自定义一个钩子useTheme,直接在其他组件中使用。代码位置:src/contexts/ThemeContext.js。importReact,{useState,useContext}来自"react";exportconstthemes={light:{type:'light',background:'#ffffff',color:'#000000',},dark:{type:'dark',背景:'#000000',颜色:'#ffffff',},};constThemeContext=React.createContext({theme:themes.dark,toggleTheme:()=>{},});exportconstThemeProvider=({children})=>{const[theme,setTheme]=useState(themes.dark);constcontext={theme,toggleTheme:()=>setTheme(theme===themes.dark?themes.light:themes.dark)}返回{children}}exportconstuseTheme=()=>{constcontext=useContext(ThemeContext);returncontext;};创建一个AppProviders,用来组创建多个上下文。代码位置:src/contexts/index.js。import{ThemeProvider}from'./ThemeContext';constAppProviders=({children})=>{return{children}}exportdefaultAppProviders;在App.js文件中实现ToggleTheme组件,添加AppProviders组件作为根组件放在最上面,这样被包裹的组件就可以使用AppProviders组件提供的属性。代码位置:src/App.js。从'./contexts'导入AppProviders;从'./components/ToggleTheme'导入ToggleTheme;导入'./App.css';constApp=()=>();导出默认应用程序;在ToggleTheme组件中,我们使用自定义的useTheme钩子来访问主题对象和toggleTheme函数。下面创建一个简单的主题开关来设置背景颜色和文本颜色。代码位置:src/components/ToggleTheme.jsx。import{useTheme}from'../contexts/ThemeContext'constToggleTheme=()=>{const{theme,toggleTheme}=useTheme();返回切换浅色/深色主题在React中使用useState和useContext切换浅色/深色主题
切换到{theme.type}模式 }exportdefaultToggleTheme;Demo演示视频示例代码地址:https://github.com/qufei1993/react-state-example/tree/usestate-usecontext-theme。useReducer+useContext实现Todos使用useReducer和useContext来完成一个Todos。这个例子非常简单,可以帮助我们学习如何实现一个简单的状态管理工具,比如Redux,它可以跨组件共享数据状态。reducer在src/reducers目录下实现了reducer需要的逻辑。定义的initialState变量和reducer函数都是为useReducerHook准备的。在这个地方,都需要导出。reducer函数是一个纯函数。了解Redux小细节的小伙伴应该对这个概念不陌生。//src/reducers/todos-reducer.jsxexportconstTODO_LIST_ADD='TODO_LIST_ADD';exportconstTODO_LIST_EDIT='TODO_LIST_EDIT';exportconstTODO_LIST_REMOVE='TODO_LIST_REMOVE';constrandomID=()=>Math.floor(Math.random()*10000);exportconstinitialState={todos:[{id:randomID(),content:'todolist'}],};constreducer=(state,action)=>{switch(action.type){案例TODO_LIST_ADD:{constnewTodo={id:randomID(),content:action.payload.content};返回{todos:[...state.todos,newTodo],}}caseTODO_LIST_EDIT:{return{todos:state.todos.map(item=>{constnewTodo={...item};if(item.id===action.payload.id){newTodo.content=action.payload.content;}returnnewTodo;})}}caseTODO_LIST_REMOVE:{返回{todos:state.todos.filter(item=>item.id!==action.payload.id),}}默认值:返回状态;}}exportdefaultreducer;Context跨组件数据共享定义TodoContextexportstate,dispatch,结合useContext自定义一个useTodohook获取信息//src/contexts/TodoContext.jsimportReact,{useReducer,useContext}from"react";importreducer,{initialState}from"../reducers/todos-reducer";constTodoContext=React.createContext(null);导出constTodoProvider=({children})=>{const[state,dispatch]=useReducer(reducer,initialState);constcontext={state,dispatch}return
{children}}exportconstuseTodo=()=>{constcontext=useContext(TodoContext);}returncontext;};//src/contexts/index.jsimport{TodoProvider}来自'./TodoContext';constAppProviders=({children})=>{return{children}}exportdefaultAppProviders;现实Todos组在TodoAdd、Todo、Todos三个组内分别都可以通过useTodo()hook获取到状态、调度。从“反应”导入{useState};从“../../contexts/TodoContext”导入{useTodo};从“../../reducers/todos-reducer”导入{TODO_LIST_ADD,TODO_LIST_EDIT,TODO_LIST_REMOVE};constTodoAdd=()=>{console.log('TodoAddrender');const[内容,setContent]=useState('');const{dispatch}=useTodo();返回setContent(e.target.value)}/>{dispatch({type:TODO_LIST_ADD,payload:{content}})}}>添加 };constTodo=({todo})=>{console.log('待办渲染');const{dispatch}=useTodo();const[isEdit,setIsEdit]=useState(false);const[content,setContent]=useState(todo.content);返回