We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本文已收录在 Github: https://github.com/beichensky/Blog 中,欢迎 Star!
Github
本文代码通过 create-react-app 脚手架进行搭建,所有的代码均可直接复制运行。
create-react-app
代码位置:react-redux-demo
本文主要讲解了 Redux 和 React-Redux 的使用,详细的概念以及设计思想请看 Redux 中文官网
React
# npx npx create-react-app my-app # npm npm init react-app my-app # yarn yarn create react-app my-app
redux
cd my-app npm install redux # or yarn add redux
是把数据从组件传到 store 的载体,是修改 store 数据的唯一来源。 是一个普通的 javascript 对象,必须包含一个 type 属性,用来通知 reducer 这个 action 需要做的操作类型。 比如:
store
javascript
type
reducer
action
{ type: 'ADD', payload: 1 }
通过 store.dispatch(action) 将 action 传给 store
store.dispatch(action)
描述 store 数据如何更新的纯函数,接受两个参数
state:store 中的 state 值,可以给 state 设置初始值
state
action:通过 store.dispatch(action) 传递的 action 对象
通过 action 的 type 类型来判断如何更新 state 数据
比如:
function reducer(state = 0, { type, payload }) { switch (type) { case "ADD": return state + payload; case "DELETE": return state - payload; default: return state; } }
将 action 和 reducer 联系到一起的对象,具有以下职责
getState()
dispatch(action)
subscribe(listener)
store 的创建方式
const store = createStore(reducer[, prevState, ehancer]);
store/index.js
import { createStore } from "redux"; // 创建 reducer 函数 ,更新 state 数据 const reducer = function(state = 0, { type }) { switch (type) { case "INCREMENT": return ++state; case "DECREMENT": return --state; default: return state; } }; // 创建 store const store = createStore(reducer); export default store;
App.js
import React, { useEffect, useReducer, useCallback } from "react"; import store from "./store"; function App() { // 模拟 forceUpdate 方法 const [, forceUpdate] = useReducer(x => x + 1, 0); useEffect(() => { // 订阅 store 监听事件 const unsubscribe = store.subscribe(() => { forceUpdate(); }); return () => { // 组件销毁时移除事件订阅 unsubscribe(); }; }, []); const increment = useCallback( // 分发 action () => store.dispatch({ type: "INCREMENT" }), [] ); const decrement = useCallback( // 分发 action () => store.dispatch({ type: "DECREMENT" }), [] ); return ( <div className="App"> <h1>Hello Redux</h1> {/* 获取当前 state 值 */} <p>count: {store.getState()}</p> <button onClick={increment}>increment</button> <button onClick={decrement}>decrement</button> </div> ); } export default App;
这个时候,计数器已经实现了,点击 increment 或者 decrement 按钮,会更新界面上的数据
increment
decrement
那假如说,此时我们可能会处理多个业务场景,比如一个是计数器,一个是 TodoList,会有两个 reducer,这个时候该如何创建呢?请看下一个 API
TodoList
API
是一个高阶函数,作用是将多个 reducer 函数按照合并生成一个 reducer 函数。 接受一个对象,返回一个 reducer 函数。对象的键可以设置任意属性名,对象的值是对应的 reducer 函数。
在使用 store 中的 state 值时,state 中的对应的属性名就是之前传给 combineReducers 方法的对象的属性名。
combineReducers
const reducer = combineReducers({ count: counterReducer, todos: todoReducer });
获取 state 时:
const state = store.getState(); // state: { count: xxx, todos: xxx }
我们在上面的例子中再加一个展示 TodoList 的功能
import { createStore, combineReducers } from "redux"; // 创建 counterReducer 函数 ,更新 state 数据 const counterReducer = function(state = 0, { type }) { switch (type) { case "INCREMENT": return ++state; case "DECREMENT": return --state; default: return state; } }; // 创建 todoReducer 函数,更新 state 数据 const todoReducer = function(state = [], { type, payload }) { switch (type) { case "INIT": return payload; case "ADD": state.push(payload); return [...state]; default: return state; } }; // 合并 reducer const reducer = combineReducers({ count: counterReducer, todos: todoReducer }); // 创建 store const store = createStore(reducer); export default store;
import React, { useEffect, useReducer, useCallback, useState } from "react"; import store from "./store"; function App() { // 模拟 forceUpdate 方法 const [, forceUpdate] = useReducer(x => x + 1, 0); const [value, setValue] = useState(""); useEffect(() => { // 订阅 store 监听事件 const unsubscribe = store.subscribe(() => { forceUpdate(); }); return () => { // 组件销毁时移除事件订阅 unsubscribe(); }; }, []); const increment = useCallback( // 分发 action () => store.dispatch({ type: "INCREMENT" }), [] ); const decrement = useCallback( // 分发 action () => store.dispatch({ type: "DECREMENT" }), [] ); const add = useCallback(() => { if (value) { // 分发 action store.dispatch({ type: "ADD", payload: value }); setValue(""); } }, [value]); // 解构 state const { count, todos } = store.getState(); return ( <div className="App"> <h1>Hello Redux</h1> <p>count: {count}</p> <button onClick={increment}>increment</button> <button onClick={decrement}>decrement</button> <br /> <br /> <input placeholder="请输入待办事项" value={value} onChange={e => setValue(e.target.value)} /> <button onClick={add}>add</button> <ul> {todos.map(todo => ( <li key={todo}>{todo}</li> ))} </ul> </div> ); } export default App;
至此,计数器和 TodoList 的功能都已经实现了
我们现在一直用的都是 redux 自己的功能,如果我想使用一些插件该怎么做呢,比如我想使用 logger 插件打印一些日志,请看下一个 API
logger
使用 applyMiddleware 可以应用插件,扩展 redux 功能。
applyMiddleware
applyMiddleware 是一个函数,接受多个参数值,返回一个高阶函数供 createStore 使用。
createStore
const ehancer = applyMiddleware(middleware1[, middleware2, middleware3, ...]);
下面我们以 redux-logger 插件为例,使用 applyMiddleware:
redux-logger
安装 redux-logger
npm install redux-logger -D # or yarn add redux-logger -D
// ... // 合并 reducer const reducer = combineReducers({ count: counterReducer, todos: todoReducer }); // 应用插件 const ehancer = applyMiddleware(logger); // 创建 store const store = createStore(reducer, ehancer); export default store;
上面我们修改数据的时候一直都是在同步状态下进行,那如果现在有一个副作用操作,需要异步执行完成才能进行 state 更新,又该怎么做呢?就要用到 redux-thunk 插件了
redux-thunk
这个插件把 store 的 dispatch 方法做了一层封装,可以接受一个函数作为 action。
dispatch
当判断当前 action 是一个函数的时候,会自动执行,并将 dispatch 作为参数传给我们。
安装 redux-thunk
npm install redux-thunk -D # or yarn add redux-thunk -D
下面我们看看 redux-thunk 的用法以及使用场景
还是在刚才的例子上,我们想要在组件加载完成之后对 TodoList 添加一些初始值,这个过程是一个异步过程
将 redux-thunk 插件应用到 store 中去 store/index.js
import thunk from "redux-thunk"; // ... // 应用插件 const ehancer = applyMiddleware(thunk, logger); // 创建 store const store = createStore(reducer, ehancer); export default store;
// ... useEffect(() => { // 派发一个异步 action,是一个函数 store.dispatch(dispatch => { setTimeout(() => { dispatch({ type: "INIT", payload: ["吃饭", "睡觉", "敲代码"] }); }, 1000); }); }, []); // ...
我们现在分发 action 的时候,都是直接 dispatch 一个对象,代码少的情况下还好,多的话可能就比较复杂,还要和 reducer 中的 type 对应,所以写起来比较麻烦,下面我们介绍一个概念:ActionCreator
ActionCreator
这不是一个 API 或者方法,只是一种思想和实现。就是通过调用一个函数生成一个对应的 action,在需要的时候我们直接调用这个函数,进行 dispatch 就可以了
const addTodo = todo => ({ type: "ADD", payload: todo });
这种写法我们 dispatch 的时候不用考虑 type ,也不用写键值对,只要传入正确的参数,就可以了。 下面我们就把刚才我们写的例子修改一下,使用这种 ActionCreator 的思想去编写 action
store/index.js 中添加 actionCreator 并导出
actionCreator
// ... const increment = () => ({ type: "INCREMENT" }); const decrement = () => ({ type: "DECREMENT" }); const initTodos = todos => ({ type: "INIT", payload: todos }); const addTodo = todo => ({ type: "ADD", payload: todo }); // 异步 action,执行完成之后调用同步 action const getAsyncTodos = () => dispatch => setTimeout(() => dispatch(initTodos(["吃饭", "睡觉", "写代码"])), 1000); export const actionCreators = { increment, decrement, getAsyncTodos, addTodo };
App.js 中 dispatch 中调用 actionCreator
import React, { useEffect, useReducer, useCallback, useState } from "react"; import store, { actionCreators } from "./store"; const { increment as inc, decrement as dec, getAsyncTodos, addTodo } = actionCreators; function App() { // 模拟 forceUpdate 方法 const [, forceUpdate] = useReducer(x => x + 1, 0); const [value, setValue] = useState(""); useEffect(() => { // 订阅 store 监听事件 const unsubscribe = store.subscribe(() => { forceUpdate(); }); return () => { // 组件销毁时移除事件订阅 unsubscribe(); }; }, []); useEffect(() => { // 派发一个异步 action,是一个函数 store.dispatch(getAsyncTodos()); }, []); const increment = useCallback( // 分发 action () => store.dispatch(inc()), [] ); const decrement = useCallback( // 分发 action () => store.dispatch(dec()), [] ); const add = useCallback(() => { if (value) { // 分发 action store.dispatch(addTodo(value)); setValue(""); } }, [value]); // 解构 state const { count, todos } = store.getState(); return ( <div className="App"> <h1>Hello Redux</h1> <p>count: {count}</p> <button onClick={increment}>increment</button> <button onClick={decrement}>decrement</button> <br /> <br /> <input placeholder="请输入待办事项" value={value} onChange={e => setValue(e.target.value)} /> <button onClick={add}>add</button> <ul> {todos.map(todo => ( <li key={todo}>{todo}</li> ))} </ul> </div> ); } export default App;
但是这个时候,感觉还是有点麻烦,每次触发 store 更新都要使用 dispatch(actionCreator(args)) 才行,能不能直接调用方法,就能触发 store 更新呢。 当然也可以了,redux 为我们提供了一个 bindActionCreators 函数
dispatch(actionCreator(args))
当然也可以了,redux
bindActionCreators
这个函数是将 dispatch 绑定到了 actionCreator 方法上,之后只要我们执行 actionCreator 就会触发 store 更新了,不用每次都 dispacth 了。
dispacth
接受两个参数:
actionCreators
dipatch
返回一个新的对象,对象的属性名是刚才传入的 actionCreators 中的属性名,属性值时包装后的方法,执行即可触发 store 更新
const finalActions = bindActionCreators({ increment: () => ({ type: 'INCREMENT }), decrement: () => ({ type: 'DECREMENT }) }, dispatch) // finalActions: { increment, decrement }
下面我们将 App.js 中的代码进行一波优化,看看最后的效果
import React, { useEffect, useReducer, useCallback, useState } from "react"; import { bindActionCreators } from "redux"; import store, { actionCreators } from "./store"; // 生成包装后的 actionCreator,执行之后就会触发 store 数据的更新 const { increment, decrement, getAsyncTodos, addTodo } = bindActionCreators( actionCreators, store.dispatch ); function App() { // 模拟 forceUpdate 方法 const [, forceUpdate] = useReducer(x => x + 1, 0); const [value, setValue] = useState(""); useEffect(() => { // 订阅 store 监听事件 const unsubscribe = store.subscribe(() => { forceUpdate(); }); return () => { // 组件销毁时移除事件订阅 unsubscribe(); }; }, []); // 初始化 TodoList useEffect(() => { getAsyncTodos(); }, []); const add = useCallback(() => { if (value) { // 分发 action addTodo(value); setValue(""); } }, [value]); // 解构 state const { count, todos } = store.getState(); return ( <div className="App"> <h1>Hello Redux</h1> <p>count: {count}</p> <button onClick={increment}>increment</button> <button onClick={decrement}>decrement</button> <br /> <br /> <input placeholder="请输入待办事项" value={value} onChange={e => setValue(e.target.value)} /> <button onClick={add}>add</button> <ul> {todos.map(todo => ( <li key={todo}>{todo}</li> ))} </ul> </div> ); } export default App;
有没有发现,到目前为止,数据变化时,我们更新 React 组件还是通过自己去添加 subscribe 订阅,一个组件还好,那在项目开发的过程中,每个组件都这么写,岂不是太麻烦了。 可以看我的下一篇博客:十分钟学会 react-redux,详细介绍了 react-redux 的用法。
react-redux
如果有写的不对或不严谨的地方,欢迎大家能提出宝贵的意见,十分感谢。
如果喜欢或者有所帮助,欢迎 Star,对作者也是一种鼓励和支持。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
本文已收录在
Github
: https://github.com/beichensky/Blog 中,欢迎 Star!写在前面
本文代码通过
create-react-app
脚手架进行搭建,所有的代码均可直接复制运行。代码位置:react-redux-demo
本文主要讲解了 Redux 和 React-Redux 的使用,详细的概念以及设计思想请看 Redux 中文官网
create-react-app
创建React
项目redux
一、Action 描述更新对象
是把数据从组件传到
store
的载体,是修改store
数据的唯一来源。是一个普通的
javascript
对象,必须包含一个type
属性,用来通知reducer
这个action
需要做的操作类型。比如:
通过
store.dispatch(action)
将action
传给store
二、Reducer 执行更新函数
描述
store
数据如何更新的纯函数,接受两个参数state
:store
中的state
值,可以给state
设置初始值action
:通过store.dispatch(action)
传递的action
对象通过
action
的type
类型来判断如何更新state
数据比如:
三、Store
将
action
和reducer
联系到一起的对象,具有以下职责state
getState()
方法获取state
dispatch(action)
方法更新state
subscribe(listener)
注册监听器;subscribe(listener)
返回的函数注销监听器store
的创建方式四、实现一个简单的 React 计数器
store.dispatch(action)
通知数据更新store
获取state
数据reducer
实现数据更新store/index.js
App.js
这个时候,计数器已经实现了,点击
increment
或者decrement
按钮,会更新界面上的数据五、combineReducers 合并 reducer
是一个高阶函数,作用是将多个
reducer
函数按照合并生成一个reducer
函数。接受一个对象,返回一个
reducer
函数。对象的键可以设置任意属性名,对象的值是对应的reducer
函数。比如:
获取
state
时:我们在上面的例子中再加一个展示
TodoList
的功能store/index.js
App.js
至此,计数器和
TodoList
的功能都已经实现了六、applyMiddleware 应用插件
使用
applyMiddleware
可以应用插件,扩展redux
功能。applyMiddleware
是一个函数,接受多个参数值,返回一个高阶函数供createStore
使用。下面我们以
redux-logger
插件为例,使用applyMiddleware
:安装
redux-logger
npm install redux-logger -D # or yarn add redux-logger -D
store/index.js
七、redux-thunk 支持异步 action
这个插件把
store
的dispatch
方法做了一层封装,可以接受一个函数作为action
。当判断当前
action
是一个函数的时候,会自动执行,并将dispatch
作为参数传给我们。安装
redux-thunk
npm install redux-thunk -D # or yarn add redux-thunk -D
下面我们看看
redux-thunk
的用法以及使用场景还是在刚才的例子上,我们想要在组件加载完成之后对
TodoList
添加一些初始值,这个过程是一个异步过程将
redux-thunk
插件应用到store
中去store/index.js
App.js
八、ActionCreator: action 创建函数
这不是一个
API
或者方法,只是一种思想和实现。就是通过调用一个函数生成一个对应的action
,在需要的时候我们直接调用这个函数,进行dispatch
就可以了比如:
这种写法我们
dispatch
的时候不用考虑type
,也不用写键值对,只要传入正确的参数,就可以了。下面我们就把刚才我们写的例子修改一下,使用这种
ActionCreator
的思想去编写action
store/index.js
中添加actionCreator
并导出App.js
中dispatch
中调用actionCreator
九、bindActionCreators
这个函数是将
dispatch
绑定到了actionCreator
方法上,之后只要我们执行actionCreator
就会触发store
更新了,不用每次都dispacth
了。接受两个参数:
actionCreators
:是一个对象,对象的属性名可以任意命名,属性值是对应的actionCreator
方法dispatch
:store
中的dipatch
属性返回一个新的对象,对象的属性名是刚才传入的
actionCreators
中的属性名,属性值时包装后的方法,执行即可触发store
更新比如:
下面我们将
App.js
中的代码进行一波优化,看看最后的效果写在后面
如果有写的不对或不严谨的地方,欢迎大家能提出宝贵的意见,十分感谢。
如果喜欢或者有所帮助,欢迎 Star,对作者也是一种鼓励和支持。
The text was updated successfully, but these errors were encountered: