redux
redux
记录一下 redux 的一些用法,如果想学习 redux,建议看,另外推荐一本写的,里面讲解了一些 react 和 redux 的原理。
start
运行如下命令,不了解 npx 的,可以看一下。
// 脚手架创建项目 npx create-react-app redux-test // 进入文件夹 cd redux-test // 启动 react npm run start
然后安装 redux:
npm i -s redux react-redux
接着把 src 下的文件都删掉几个,只剩两个文件:
src/ |--index.js |--serviceworker.js
redux and react-redux
打开上面留下的 index.js,删掉里面的代码,敲下自己的代码,然后刷新网页。
// index.js import react, { component } from 'react' import reactdom from 'react-dom' import * as serviceworker from './serviceworker' import { createstore } from 'redux' import { connect, provider } from 'react-redux' // actiontypes const num_add = 'num_add'; // actioncreator function addnum () { return { type: num_add } } // reducer const initialstate = { num: 0 } function counter (state = initialstate, action) { switch (action.type) { case num_add: return { num: state.num + 1 } default: return state } } // store const store = createstore(counter) // app class app extends component { render() { return ( <div> {this.props.num} <button onclick={this.props.onclickadd}>add</button> </div> ); } } // connect(mapstatetoprops, mapdispatchtoprops)(component) const mapstatetoprops = state => { return { num: state.num } } const mapdispatchtoprops = dispatch => { return { onclickadd: () => { dispatch(addnum()) } } } app = connect(mapstatetoprops, mapdispatchtoprops)(app) // provider 传入 store reactdom.render( <provider store={store}> <app /> </provider>, document.getelementbyid('root')); serviceworker.unregister();
mapdispatchtoprops
connect
中的mapdispatchtoprops
可以是一个函数,也可以是一个对象,函数写法如上。
当mapdispatchtoprops
是一个对象时,redux 会把它里面的属性作为actioncreator
交给dispatch
使用,简单来说,就是 redux 帮你把对象里面的属性封装为函数型的mapdispatchtoprops
,写法如下:
// index.js ... // app class app extends component { render() { return ( <div> {this.props.num} // 这里也进行了修改 // onclickadd -> addnum <button onclick={this.props.addnum}>add</button> </div> ); } } ... // connect app = connect( state => state, { addnum } )(app) ...
@connect
connect
可以使用装饰器的写法。
// 装饰器的 babel 插件 npm i -s @babel/plugin-proposal-decorators
然后进行配置 plugin,这里有两种配置方法:
-
使用 create-react-app 的配置
// 暴露配置 npm run eject // 会进行确认 are you sure you want to eject? this action is permanent.(y/n) y
然后会看到文件夹内多了一些东西,打开项目根路径下的 package.json 文件,找到 babel 配置项:
// package.json ... "babel": { "presets": [ "react-app" ], "plugins": ["@babel/plugin-proposal-decorators", { "legacy": true }] }, ...
-
使用独立文件配置 babel:
打开项目根路径下的 package.json 文件,找到 babel 配置项,将他删掉:
// package.json ... // 把这个删掉 "babel": { "presets": [ "react-app" ], }, ...
然后在项目根目录创建
.babelrc
文件。// .babelrc { "presets": ["react-app"], "plugins": [ ["@babel/plugin-proposal-decorators", { "legacy": true }] }
配置完 babel 之后,打开 src 下的 index.js 进行修改:
// index.js ... // app // 装饰器写法 @connect( state => state, { addnum } ) class app extends component { render() { return ( <div> {this.props.num} <button onclick={this.props.addnum}>add</button> </div> ); } } ...
注意:对于装饰器的支持只是实验性的,未来可能会改动。
redux-thunk
可以使用中间件 redux-thunk 进行异步操作,它可以让actioncreator
不返回action
对象,而是返回一个函数,可以在函数内封装逻辑。
function incrementifodd() { // 接收两个参数 // getstate() 可以拿到 store 中的 state return (dispatch, getstate) => { const { counter } = getstate(); if (counter % 2 === 0) { return; } dispatch(increment()); }; }
首先还是安装:
npm i -s redux-thunk
然后打开 index.js 进行修改:
// index.js ... import {createstore, applymiddleware} from 'redux' import thunk from 'redux-thunk' ... // store const store = createstore(counter, applymiddleware(thunk)) ...
异步操作有很多,这里将 num 的增加推迟为 1s 后才进行:
// index.js // actioncreator function addnum () { return { type: num_add } } function addnumasync () { return dispatch => { settimeout(() => { dispatch({type: num_add}) }, 1000) } } // app @connect( state => state, { addnum, addnumasync } ) class app extends component { render() { return ( <div> {this.props.num} <button onclick={this.props.addnum}>add</button> <button onclick={this.props.addnumasync}>add after 1s</button> </div> ); } }
combinereducers
当你有多个 reducer 时,可以使用combinereducers
,进行合并。
import { createstore, combinereducers } from 'redux' const allreducer = combinereducers({ reducerone, reducerstwo }) store = createstore(allreducer)
bindactioncreators
把 actioncreator 传到一个子组件中,却不想让这个组件觉察到 redux 的存在,而且不希望把 dispatch 或 store 传给它时,可以使用bindactioncreators
。
这里使用官方文档的例子:
// todoactioncreators.js export function addtodo(text) { return { type: 'add_todo', text }; } export function removetodo(id) { return { type: 'remove_todo', id }; }
// somecomponent.js import { component } from 'react'; import { bindactioncreators } from 'redux'; import { connect } from 'react-redux'; import * as todoactioncreators from './todoactioncreators'; console.log(todoactioncreators); // { // addtodo: function, // removetodo: function // } class todolistcontainer extends component { constructor(props) { super(props); const {dispatch} = props; // 这是一个很好的 bindactioncreators 的使用示例: // 你想让你的子组件完全不感知 redux 的存在。 // 我们在这里对 action creator 绑定 dispatch 方法, // 以便稍后将其传给子组件。 this.boundactioncreators = bindactioncreators(todoactioncreators, dispatch); console.log(this.boundactioncreators); // { // addtodo: function, // removetodo: function // } } componentdidmount() { // 由 react-redux 注入的 dispatch: let { dispatch } = this.props; // 注意:这样是行不通的: // todoactioncreators.addtodo('use redux') // 你只是调用了创建 action 的方法。 // 你必须要同时 dispatch action。 // 这样做是可行的: let action = todoactioncreators.addtodo('use redux'); dispatch(action); } render() { // 由 react-redux 注入的 todos: let { todos } = this.props; return <todolist todos={todos} {...this.boundactioncreators} />; // 另一替代 bindactioncreators 的做法是 // 直接把 dispatch 函数当作 prop 传递给子组件,但这时你的子组件需要 // 引入 action creator 并且感知它们 // return <todolist todos={todos} dispatch={dispatch} />; } } export default connect(state => ({ todos: state.todos }))(todolistcontainer)
备注
个人学习 redux 的感受,看,不如动手去敲。
上一篇: AJAXCALL