redux
记录一下 redux 的一些用法,若是想学习 redux,建议看官方文档,另外推荐一本huzidaha写的react小书,里面讲解了一些 react 和 redux 的原理。javascript
start
运行以下命令,不了解 npx 的,能够看一下阮一峰的文章。html
// 脚手架建立项目 npx create-react-app redux-test // 进入文件夹 cd redux-test // 启动 react npm run start
而后安装 redux:java
npm i -S redux react-redux
接着把 src 下的文件都删掉几个,只剩两个文件:react
src/ |--index.js |--serviceWorker.js
redux and react-redux
打开上面留下的 index.js,删掉里面的代码,敲下本身的代码,而后刷新网页。git
// 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
能够是一个函数,也能够是一个对象,函数写法如上。github
当mapDispatchToProps
是一个对象时,redux 会把它里面的属性做为actionCreator
交给dispatch
使用,简单来讲,就是 redux 帮你把对象里面的属性封装为函数型的mapDispatchToProps
,写法以下:shell
// 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
可使用装饰器的写法。npm
// 装饰器的 babel 插件 npm i -S @babel/plugin-proposal-decorators
而后进行配置 plugin,这里有两种配置方法:json
-
使用 create-react-app 的配置redux
// 暴露配置 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 的感觉,看,不如动手去敲。