react-redux使用方法
redux简介
-
什么的redux?
官方解释:redux 是 js 应用的可预测状态的容器。 可以理解为全局数据状态管理工具(状态管理机),用来做组件通信等。
在react-redux里面,可以把它理解成vue里面的全局状态管理机制
vuex
-
为什么使用redux?
-
state
前端中的state就是数据,就是一个对象。redux中的state是不能直接修改的,只能通过action来修改,相当于我们在单例中定义setter方法。
-
action
redux 将每一个更改动作描述为一个action,要更改state中的内容,你需要发送action。一个action是一个简单的对象,用来描述state发生了什么变更。
const INCREMENT = 'INCREMENT' const incrementAction = {"type": INCREMENT, "count": 2}
-
reducer
数据
state
,指示action
都有了那么就是实现了。reducer就是根据action来对state进行操作。const calculate = (state: ReduxState = initData, action: Action ) => { switch (action.type) { case INCREMENT: return {num: state.num + action.count} case REDUCE: return {num: state.num - action.count} default: return state } } export {calculate}
通过reducer操作后返回一个新的state,比如这里根据action的type分别对state.num进行加减.
-
store
store就是整个项目保存数据的地方,并且只能有一个。创建store就是把所有reducer给它。
import { createStore, combineReducers } from "redux"; import { calculate } from "./calculate"; // 全局你可以创建多个reducer 在这里统一在一起 const rootReducers = combineReducers({calculate}) // 全局就管理一个store export const store = createStore(rootReducers)
-
dispatch
store.dispatch()
是组件发出action的唯一方法。store.dispatch(incrementAction);
通过store调用
incrementAction
,那么就直接把store里的数据修改了。
文件目录
*---store // 存放redux,数据,以及action
*---store子目录
actions // 存放action的文件夹
reducers // 存放reducer的文件夹
actionTypes.js // 存放所有的actionType
index.js // store的入口文件
安装过程
$ npm install redux react-redux --save
$ cnpm install redux react-redux --save
$ yarn add redux react-redux
使用步骤
-
先在index.js中引入react-redux定义的Provider组件,并包裹在App组件外部
import React from 'react'; import ReactDOM from 'react-dom'; import { Provider } from 'react-redux'; import App from './App'; ReactDOM.render( <Provider> <App /> </Provider>, document.getElementById('root') );
connect方法生成容器组件时,我们定义了一堆的逻辑和状态,而只有顶层容器能拿到store的时候才有意义,因此需要让容器组件拿到store的state对象。
React-Redux 提供Provider组件,可以让组件树中的任意一个容器组件拿到state。
-
接着在store目录下的index.js中创建一个store,并抛出
import { createStore, combineReducers } from 'redux'; // createStore方法是用来创建store的,combineReducers方法是用来合并多个reducer的 // 创建根reducer,利用combineReducers合并多个reducer,此处还未定义reducer,所以暂空 const rootReducer = combineReducers({ }) // 创建初始化的state,初始化为一个空对象即可,默认的数据建议都写在reducer上 const initializeState = {}; // 定义初始化的state // 创建store,第一个参数是根reducer,第二个参数可以是初始化的state,也可以是别的,暂且不提 const store = createStore(rootReducer,initializeState); // 抛出store export default store;
-
在src/index.js中引入store,并且在Provider组件上使用
/**之前的代码**/ import store from './store'; ReactDOM.render( <Provider store={store}> <App /> </Provider>, document.getElementById('root') );
-
接着可以先定义数据,即定义reducer,在src/store/reducers目录下,新建reducer,例如countReducer.js
// 定义初始化的数据,根据实际数据即可 const initializeState = { count: 1 } // 定义reducer,第一个参数为state,赋予默认值为上边定义的initializeState, // 第二个参数为action,并return一个state // 并且抛出这个countReducer export default function countReducer(state = initializeState,action) { return state; }
-
在src/store/index.js中引入定义的countReducer,并合并到rootReducer中
// 引入countReducer, import countReducer from './reducers/countReducer'; // 将countReducer合并到rootReducer上,并使用原有名称 const rootReducer = combineReducers({ countReducer }) // 也可以给countReducer改名,如下,改名成为count const rootReducer = combineReducers({ count: countReducer })
接着只需要将组件改造一下,就可以使用reducer上边的数据了
-
在src/APP.js中,或者是你需要使用store中数据的组件中,引入react-redux提供的connect方法
import { connect } from 'react-redux';
并且在抛出之前,定义获取数据的方法mapStateToProps
// 定义方法mapStateToProps,参数为state,并且返回一个对象,对象内定义需要获取的store内的数据, // 由于是使用的countReducer中的数据,所以需要使用state.countReducer.属性名 function mapStateToProps(state) { return { count: state.countReducer.count } }
mapStateToProps是一个函数。它的作用就是建立一个从(外部的)state对象到(UI 组件的)props对象的映射关系。也就是拿一部分有用的state,映射到当前容器组件的props中,以便于通过props直接引用。
mapStateToProps会订阅 Store,每当state更新的时候,就会自动执行,重新计算 UI 组件的参数,从而触发 UI 组件的重新渲染。
作为函数,mapStateToProps执行后应该返回一个对象,里面的每一个键值对就是一个映射
-
接着,在抛出的时候调用connect方法改造当前组件
// connect的第一个参数为数据,即mapStateToProps方法 // 接着在第二个括号内传入当前需要被改造的组件 export default connect(mapStateToProps)(App);
React-Redux 提供了connect方法,用于将 UI 组件包装成容器组件。
但是正如我们刚才提到的,顶层容器是要有状态有业务逻辑的,因此在connect方法生成顶层容器时,我们就应该把所需要的状态和业务逻辑定义好并提供给connect。
因此,connect方法如上代码其中,connect方法接受两个参数:mapStateToProps和mapActionToProps(也有叫mapDispatchToProps)。它们定义了 UI 组件的业务逻辑。前者负责输入逻辑,即将state映射到 UI 组件的参数(props),后者负责输出逻辑,即将用户对 UI 组件的操作映射成 Action。
-
然后,我们在被改造的组件内就可以通过this.props.属性名获取store中的数据了,例如我在mapStateToProps方法中返回的是count数据,所以我在App组件中使用this.props.count即可
class App extends Component { render() { return ( <div> {this.props.count} </div> ); } }
获取到数据之后,接着应该是修改仓库内的数据
-
修改数据首先需要定义一个dispatch,在redux中,修改数据必须通过dispatch提交一个action来进行,在src/store/actions目录下新建countAction.js
在countAction.js中定义addCount方法,并return一个对象,对象上有一个type属性,这个属性对应的是一个常量字符串,这个字符串定义在src/store/actionTypes.js中,主要是为了可以公共的管理,因为同一个常量需要在两个地方使用
// countAction.js import { ADD_COUNT } from '../actionTypes' export function addCount() { return { type: ADD_COUNT } } // actionTypes.js export const ADD_COUNT = 'ADD_COUNT';
-
接着在src/store/reducers/countReducer.js中,引入ADD_COUNT常量,并使用switch语句,或者if语句对action.type进行判断,当触发这个action的时候,让当前这个reducer的state.count加1
import { ADD_COUNT } from '../actionTypes'; export default function countReducer(state = initializeState,action) { switch (action.type) { case ADD_COUNT: return { count: state.count + 1 }; default: return state; } }
紧接着,要在组件中使用这个addCount方法,提交这个dispatch,触发这个action
-
在App.js或者是使用store的组件中,首先引入addCount方法,然后在mapStateToProps的下边,在定义一个mapActionToProps方法,接着在connect的第二个参数位置,传入这个方法即可
-
在mapActionToProps方法中,第一个参数为dispatch,return一个对象,对象上定义方法
方法名自定义即可,接着触发这个方法的时候,触发dispatch(),并传入引入的addCount()方法,需要加括号调用,因为只有调用才会返回一个对象,( 或者addCount直接是一个对象,而不是一个函数 )import { addCount } from './store/actions/countAction' /** 其余代码 **/ /** mapStateToProps **/ function mapActionToProps(dispatch) { return { addCount: () => dispatch(addCount()) } } export default connect(mapStateToProps,mapActionToProps)(App);
此时,可以在App组件内调用this.props.addCount()方法来修改store中的数据了
首先在src/store/actions/countAction.js中定义一个新的方法,当然,这个方法用到的REDUCE_COUNT常量要定义在src/store/actionTypes.js中
// src/store/actions/countAction.js export function reduceCount(num) { return { type: REDUCE_COUNT, num } } // src/store/actionTypes.js export const REDUCE_COUNT = 'REDUCE_COUNT';
-
而且,在src/store/reducers/countReducer.js中,需要通过switch进行判断action,并执行操作
由于我们在action中定义的对象的属性是num,所以在reducer中进行参数使用的时候,也是使用action.num
// src/store/reducers/countReducer.js import { ADD_COUNT, REDUCE_COUNT } from '../actionTypes' export default function countReducer(state = initializeState,action) { switch (action.type) { case ADD_COUNT: return { count: state.count + 1 }; // 新增 case REDUCE_COUNT: return { count: state.count - action.num }; default: return state; } }
-
在App.js中使用reduceCount方法
import { addCount, reduceCount } from './store/actions/countAction'; /** 其余代码 **/ /** mapStateToProps **/ function mapActionToProps(dispatch) { return { addCount: () => dispatch(addCount()), reduceCount: (num) => dispatch(reduceCount(num)) } }
接着在组件中直接调用this.props.reduceCount()方法,并传入一个参数即可
-
最后,为了遵循react的规范,我们需要在给组件定义props的时候,规定props的类型
首先在App.js或者使用store的组件中,引入prop-types
然后在文件最末尾,抛出之前,定义所有的props的类型
import PropTypes from 'prop-types'; /** 其余代码 **/ App.propTypes = { count: PropTypes.number.isRequired, addCount: PropTypes.func.isRequired, reduceCount: PropTypes.func.isRequired }