为什么我想要使用 redux?
前段时间初步上手了 react, 最近在使用 react 的过程中发现对于组件之间通信的需求比较迫切, 尤其是在 axios 异步请求后端数据的时候, 这样的需求是特别强烈的! 举个例子:
- // 厂家报告到货
- class ReportArrivalGoods extends React.Component{
- constructor(props){
- super(props);
- this.state = {
- columns:tableHead.ReportArrivalGoods,// 这里是初始化的表头, 内容较多, 不显示出来了
- data: []
- };
- }
- componentDidMount(){
- axios(
- {
- method: 'get',
- url: 'http://172.89.1.79/LogistiCSSporadicBoardBackEnd/index.asmx/ReportArrivalGoods'
- })
- .then(
- res => {
- this.setState(
- data:NewState
- );
- }
- )
- .catch(
- error => {
- console.log(error);
- }
- );
- }
- render(){
- return(
- <Table
- style={
- {
- width: '100%'
- }
- }
- columns={
- this.state.columns
- }
- data={
- this.state.data
- }
- border={
- true
- }
- maxHeight={
- tableMaxHeight
- }
- />
- );
- }
- }
我们聚焦于下面的 componentDidMount() 函数
- componentDidMount(){
- axios(
- {
- method: 'get',
- url: 'http://172.89.1.79/LogisticsSporadicBoardBackEnd/index.asmx/ReportArrivalGoods'
- })
- .then(
- res => {
- this.setState(
- data:NewState
- );
- }
- )
- .catch(
- error => {
- console.log(error);
- }
- );
- }
这是大家都很熟悉的 react 生命周期钩子函数, 我做了这样一件事: 页面渲染完向后台请求数据并显示到页面上. 但是用过 axios 的都知道, 他跟 Ajax 一样, 都是异步的, 也就说, 你发处请求后就会立即执行后面的代码了, 等你请求结果回来了才会执行 then() 和 catch(). 一开始我简单粗暴的直接把整个函数体写进了钩子中, 实现是没问题了, 可是要使用钩子函数请求不同数据的组件有 5 个, 要是每个组件都写这么一长串代码, 一方面不好看, 另一方面重复代码太多了. 于是我想到了把这段函数体封装起来, 于是就有下面的代码
- //webservice 数据请求函数
- function AxiosGet(desinationURL){
- axios(
- {
- method: 'get',
- url: desinationURL
- })
- .then(
- res => {
- );
- }
- )
- .catch(
- error => {
- console.log(error);
- }
- );
- }
可是要是封装了怎么设置我组件的 state 呢? 机智的我想到了个办法, 在组件内部创建一个带参函数 modifyState(), 在使 AxiosGet() 函数时将 modifyState() 函数作为参数传入 AxiosGet() 并在 AxiosGet() 内部将 res.data 作为参数传 modifyState() 从而达到 setstate 的目的. 说起来有点绕, 用代码说话
- //webservice 数据请求函数
- function AxiosGet(desinationURL,ApplyNewState){
- axios(
- {
- method: 'get',
- url: desinationURL
- })
- .then(
- res => {
- ApplyNewState(res.data);
- }
- )
- .catch(
- error => {
- console.log(error);
- }
- );
- }
上面是组件外部的数据请求函数, 下面的是组件内部的钩子函数和用于传入获取数据的函数
- modifyState(NewState){
- this.setState(
- {
- data:NewState
- }
- );
- }
- componentDidMount(){
- AxiosGet('http://172.89.1.79/LogisticsSporadicBoardBackEnd/index.asmx/ReportArrivalGoods',
- this.modifyState
- );
- }
恩, 明眼人应该已经看懂了, 我巧妙的通过在组件内部将函数传出的方式完成了 state 的更新, 问题虽然解决了, 但是这样操作着实麻烦, 要是能在组件外部更新组件的 state 就好了. 有人要说了, 状态提升啊! 为所有的组件创建一个父组件, 在父组件中统一更新状态并通过 props 的形式传入子组件. 恩, 这确实是个办法, 但是父组件就没有表达出了特别的意思了, 就好像 div 一样, 没有语义化, 有没有更好的方式呢? 百度搜搜 react 的 state, 来了来了, 他来了, 灯灯灯灯! redux 闪亮登场!
Redux 使用七步走
此处对于 redux 的知识不做讲解了, 我懂得也就那样, redux 官网上教程很清晰, 可以直接过去学, 我主要讲讲 redux 在 react 中的使用. 如标题所说, 只需要七步. 众所周知, 在 redux 中, 最重要的东西就三个
{
动作: action,
动作处理函数: reducer,
状态仓库: store
}
想必 redux 官方教程过了一遍的人都能轻松理清其中关系与具体运行吧, 那么问题来了, 怎么在 react 中把组件们的 state 和 store 给他绑定上呢? 网上帖子一大堆, 但是跟我之前那篇中说的一样, 抄来抄去, 没有真正讲到小白的点子上, 有的还抄错了导致读者误解, 这里我从小白视角做出最贴切的讲解, 相信大家听完手敲一遍也就懂了. 我是用 create-react-App 搭建的开发环境, 这里不做赘述. 话不多说, 上代码! 目录结构如图所示:
第一步: 安装依赖:
NPM i redux redux-react -s
第二步: 创建 action
- // /actions/test.JS
- export const PLUS='PLUS';
- export function plusActionCreator(){
- return{
- type:PLUS
- }
- }
第三步: 创建 reducer
- // /reducers/test.JS
- import {PLUS} from '../actions/test'
- const initState={count:0};
- export function plusReducer(state=initState,action){
- switch(action.type){
- case PLUS:{
- return
- {
- count:state.count+1;// 此处返回的 state 只是 plusReducer 这个小范围内的 state, 理解这一点很重要!!!
- }
- }
- default:return state;
- }
- }
第四步: 创建 store
- // /index.JS
- const reducer=combineReducers({
- plus:plusReducer
- });// 这里需要传入 JSON 对象才行, 对象名代表来自哪个 reducer
- var store=createStore(reducer);
上面是 redux 部分, 大家应该很熟悉了, 此时也到了最重要的部分: react 和 redux 的结合. 需要说明一下, react 的组件我们现在分为展示组件和容器组件两类, 展示组件负责界面呈现, 容器组件负责为展示组件管理 state. 展示组件的所有 state 通过 props 传入.
第五步: 创建展示组件
- // /index.JS
- function Test (props){
- return(
- <div>
- <p > 总计数:{props.count}</p>
- <button onClick={props.plus}> 加一 </button>
- </div>
- );
- }
第六步: 通过 react-redux 提供的 connect 方法生成容器组件
- // /index.JS
- const CollectionComponent=connect(
- (state)=>{
- return{
- count:state.plus.count// 这里需要注意, 各个小版块的 state 是通过 combinereducers 中命名的 JSON 对象名做了分隔的
- // 此处的 JSON 对象 count 与展示组件中的 props.count 是对应关系
- }
- },
- (dispatch)=>{
- return{
- plus:bindActionCreators(plusActionCreator,dispatch)
- // 此处的 JSON 对象 plus 与展示组件中的 props.plus 方法也是对应关系
- } } )(Test);// 此处的 Test 与展示组件名也是对应关系
这里很关键, 首先 const CollectionComponent, 这个 CollectionComponent 就是我们所需要的容器组件.
connect(read,write)(destination) 函数有三个参数 read,write 和 destionation(抽象名, 为了方便理解这么叫),read 的目的就是从 store 中返回我们要的 state,write 的目的是传入 action 来更新 state,destination 用来绑定到指定的展示组件.
第七步: 通过 react-redux 提供的 Provider 组件搭建 react 和 redux 数据交互的桥梁
- // /index.JS
- ReactDOM.render(
- <Provider store={store}>// 这里把之前创建的 store 传给 Provider, 这样 store 中的 state 就能传达到容器组件中了
- <CollectionComponent/>// 由于第六步忠已经把展示组件绑定到了容器组件上了, 所以此处只需写容器组件即可
- </Provider>
- ,document.getElementById('root'));
OVER! 通过这个小 demo 即可完成一个简单的累加器. 最后贴上完整源码, 嘿嘿嘿, yes!
- // /reducers/test.JS
- import {PLUS} from '../actions/test'
- const initState={count:0};
- export function plusReducer(state=initState,action){
- switch(action.type){
- case PLUS:{
- return {
- count:state.count+1
- }
- }
- default:return state;
- }
- }
- // /actions/test.JS
- export const PLUS='PLUS';
- export function plusActionCreator(){
- return{
- type:PLUS
- }
- }
- // /index.JS
- //react
- import React from 'react'
- import ReactDOM from 'react-dom'
- //redux
- import {connect,Provider} from 'react-redux'
- import {bindActionCreators,combineReducers,createStore} from 'redux'
- import {plusReducer} from './reducers/test'
- import {plusActionCreator} from './actions/test'
- // 呈现部分
- function Test (props){
- return(
- <div>
- <p > 这里是 count:{props.count}</p>
- <button onClick={props.plus}> 加一 </button>
- </div>
- );
- }
- //redux 部分
- const reducer=combineReducers({plus:plusReducer});// 这里需要传入 JSON 对象才行
- var store=createStore(reducer);
- const CollectionComponent=connect(
- (state)=>{
- return{
- count:state.plus.count// 这里需要注意, 各个小版块的 state 是
- // 通过 combinereducers 中命名的 JSON 对象名做了分隔的
- }
- },
- (dispatch)=>{
- return{
- plus:bindActionCreators(plusActionCreator,dispatch)
- }
- }
- )(Test);
- ReactDOM.render(
- <Provider store={store}>
- <CollectionComponent/>
- </Provider>
- ,document.getElementById('root'));
最后再给个小彩蛋吧, 今天刚捣鼓的 react-router 的小 demo, 也是嵌套在上面的代码中的.
- //react
- import React from 'react'
- import ReactDOM from 'react-dom'
- //redux
- import {connect,Provider} from 'react-redux'
- import {bindActionCreators,combineReducers,createStore} from 'redux'
- import {plusReducer} from './reducers/test'
- import {plusActionCreator} from './actions/test'
- //router
- import { BrowserRouter,Switch,Route,NavLink } from 'react-router-dom';
- // 呈现部分
- function Test (props){
- return(
- <div>
- <p > 这里是 count:{props.count}</p>
- <button onClick={props.plus}> 加一 </button>
- </div>
- );
- }
- //redux 部分
- const reducer=combineReducers({plus:plusReducer});// 这里需要传入 JSON 对象才行
- var store=createStore(reducer);
- const CollectionComponent=connect(
- (state)=>{
- return{
- count:state.plus.count// 这里需要注意, 各个小版块的 state 是
- // 通过 combinereducers 中命名的 JSON 对象名做了分隔的
- }
- },
- (dispatch)=>{
- return{
- plus:bindActionCreators(plusActionCreator,dispatch)
- }
- }
- )(Test);
- ReactDOM.render(
- <Provider store={store}>
- <BrowserRouter>
- <Switch>
- <Route path="/class/classmates">
- <div>QianYingLi,HaoWu,ZhouHuiFan</div>
- <NavLink to="/class" activeClassName="fillInClassNameHere">
- redirect to class
- </NavLink>
- </Route>
- <Route path="/class">
- <div > 高一二 </div>
- <NavLink to="/class/classmates" activeClassName="hurray">
- redirect to classmates
- </NavLink>
- </Route>
- <Route path="/">
- <CollectionComponent/>
- </Route>
- </Switch>
- </BrowserRouter>
- </Provider>
- ,document.getElementById('root'));
来源: https://www.cnblogs.com/wuhao19950102/p/11777646.html