redux应该这样学

一.什么是redux

官方解释:Redux is a predictable state container for JavaScript apps. 意思就是Redux是js应用的 一种可预测的状态容器。

二.为什么使用redux?

图片[1]-redux应该这样学-一一网
没有使用Redux的情况,如果两个组件(非父子关系)之间需要通信的话,可能需要多个中间组件为他们进行消息传递,这样既浪费了资源,代码也会比较复杂。

Redux中提出了单一数据源Store 用来存储状态数据,所有的组件都可以通过Action修改Store,也可以从Store中获取最新状态。使用了redux就可以完美解决组建之间的通信问题

三.怎么使用redux?

redux官方图片

文字解释
React Component: 借书的人

Action Creator: 大喇叭 (用来通知图书管理员)

Store: 图书管理员

Reducer: 小本本 (图书管理员大爷记性不好,需要用小本本记录图书信息

借书的人(ReactComponent)说了一句话(Action Creator),向图书馆管理员(Store)借一本书,可是图书馆管理员年纪大了啊记不住啊,便掏出了自己的小本本(Reducers)。看了看知道了那本书有没有,在哪,怎么样。这样一来管理员就拿到了这本书,再把这本书交给了借书人。

翻译过来就是:

组件想要获取State, 用ActionCreator创建了一个请求交给Store,Store借助Reducer确认了该State的状态,Reducer返回给Store一个结果,Store再把这个State转给组件。

四.通过案例TodoList学习redux

安装redux:

npm install redux --save
# 或
yarn add redux
复制代码

搭建项目结构
项目目录如图所示

src/index.js 入口文件

import React from "react";
import ReactDOM from "react-dom";
import App from "./TodoList";
ReactDOM.render(<App></App>, document.getElementById("root"));
复制代码

src/TodoList.js

import React, { Component } from "react";
import "antd/dist/antd.css";
import { Input, Button, List } from "antd";
import store from "./store";
import {
  changeValue,
  addListItem,
  removeListItem,
  getList,
} from "./store/actionCreators";

class App extends Component {
  constructor(props) {
    super(props);
    this.state = store.getState();
    store.subscribe(() => this.storeChange());
  }
  storeChange() {
    this.setState(store.getState());
  }
  componentDidMount() {
    const action = getList();
    store.dispatch(action);
  }
  render() {
    return (
      <div style={{ margin: "10px" }}>
        <div>
          <Input
            placeholder={this.state.inputValue}
            style={{ width: "250px", marginRight: "10px" }}
            onChange={(e) => this.changeValue(e)}
          />
          <Button type="primary" onClick={() => this.addListItem()}>
            增加
          </Button>
        </div>
        <div style={{ margin: "10px", width: "300px" }}>
          <List
            bordered
            dataSource={this.state.list}
            renderItem={(item, index) => (
              <List.Item onClick={(item) => this.removeListItem(item, index)}>
                {item}
              </List.Item>
            )}
          />
        </div>
      </div>
    );
  }
  changeValue(e) {
    const action = changeValue(e.target.value);
    store.dispatch(action);
  }
  addListItem() {
    const action = addListItem();
    store.dispatch(action);
  }
  removeListItem(item, index) {
    const action = removeListItem(index);
    store.dispatch(action);
  }
}

export default App;
复制代码

src/store/index.js redux 入口文件

import { createStore, applyMiddleware, compose } from "redux";
import reducer from "./reducer";
import thunk from "redux-thunk";
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__
  ? window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
  : compose;

const enhancer = composeEnhancers(applyMiddleware(thunk));
const store = createStore(reducer, enhancer);
export default store;
复制代码

src/store/actionCreators action存放

import {
  CHANGE_VALUE,
  ADD_LIST_ITEM,
  REMOVE_LIST_ITEM,
  GET_ACTION,
} from "./actionTypes";
import axios from "axios";
export const changeValue = (value) => {
  return {
    type: CHANGE_VALUE,
    value,
  };
};
export const addListItem = (value) => {
  return {
    type: ADD_LIST_ITEM,
    value,
  };
};
export const removeListItem = (value) => {
  return {
    type: REMOVE_LIST_ITEM,
    value,
  };
};
export const getAction = (data) => {
  return {
    type: GET_ACTION,
    value: data,
  };
};
export const getList = () => {
  return (dispatch) => {
    axios
      .get(
        "https://www.fastmock.site/mock/6f4fec629859811f980f52a147ea3236/xbb_pl/xbb"
      )
      .then((res) => {
        const data = res.data;
        const action = getAction(data);
        dispatch(action);
      });
  };
};
复制代码

src/store/actionTypes 变量储存文件

export const CHANGE_VALUE = "changeValue";
export const ADD_LIST_ITEM = "addListItem";
export const REMOVE_LIST_ITEM = "removeListItem";
export const GET_ACTION = "getAction";
复制代码

src/store/reducer.js

import {
  ADD_LIST_ITEM,
  REMOVE_LIST_ITEM,
  CHANGE_VALUE,
  GET_ACTION,
} from "./actionTypes";
const defaultState = {
  inputValue: "Write Something",
  list: [
    "早8点开晨会,分配今天的开发工作",
    "早9点和项目经理作开发需求讨论会",
    "晚5:30对今日代码进行review",
  ],
};

export default (state = defaultState, action) => {
  if (action.type === CHANGE_VALUE) {
    const newState = JSON.parse(JSON.stringify(state));
    newState.inputValue = action.value;
    return newState;
  }
  if (action.type === ADD_LIST_ITEM) {
    console.log(state);
    let newState = JSON.parse(JSON.stringify(state));
    newState.list.push(newState.inputValue); //push新的内容到列表中去
    return newState;
  }
  if (action.type === REMOVE_LIST_ITEM) {
    let newState = JSON.parse(JSON.stringify(state));
    newState.list.splice(action.value, 1); //push新的内容到列表中去
    return newState;
  }
  if (action.type === GET_ACTION) {
    let newState = JSON.parse(JSON.stringify(state));
    newState.list = action.value.list;
    return newState;
  }
  return state;
};
复制代码

五. 使用redux会遇到的坑

store必须是惟一的

store必须是唯一的,多个store是坚决不允许,只能有一个store空间

只有store能改变自己的内容,Reducer不能

Reudcer只是返回了更改的数据,但是并没有更改store中的数据,store拿到了Reducer的数据,自己对自己进行了更新。

Reducer必须是纯函数

如果函数的调用参数相同,则永远返回相同的结果。它不依赖于程序执行期间函数外部任何状态或数据的变化,必须只依赖于其输入参数。

六.redux实际开发中的小技巧

将Action Types提取成常量

写Redux Action的时候,我们写了很多Action的派发,产生了很多Action Types,如果需要Action的地方我们就自己命名一个Type,会出现两个基本问题:

  • 这些Types如果不统一管理,不利于大型项目的服用,设置会长生冗余代码。
  • 因为Action里的Type,一定要和Reducer里的type一一对应在,所以这部分代码或字母写错后,浏览器里并没有明确的报错,这给调试带来了极大的困难。

那我会把Action Type单独拆分出一个文件。在src/store文件夹下面,新建立一个actionTypes.js文件,然后把Type集中放到文件中进行管理。

export const  CHANGE_INPUT = 'changeInput'
export const  ADD_ITEM = 'addItem'
export const  DELETE_ITEM = 'deleteItem'
复制代码

好处

1.可以避免冗余代码

2.如果我们写错了常量名称,程序会直接在浏览器和控制台报错,可以加快开发效率,减少找错时间。

将所有的Redux Action放到一个文件里进行管理

七.redux中间件redux-thunk

Redux-thunk这个Redux最常用的插件。什么时候会用到这个插件那?比如在Dispatch一个Action之后,到达reducer之前,进行一些额外的操作,就需要用到middleware(中间件)。在实际工作中你可以使用中间件来进行日志记录、创建崩溃报告,调用异步接口或者路由。 这个中间件可以使用是Redux-thunk来进行增强(当然你也可以使用其它的),它就是对Redux中dispatch的加强,

1.安装Redux-thunk组件

npm install --save redux-thunk
复制代码

2.配置Redux-thunk组件

引入applyMiddleware,如果你要使用中间件,就必须在redux中引入applyMiddleware

import { createStore , applyMiddleware } from 'redux'
复制代码

引入redux-thunk库

import thunk from 'redux-thunk'
复制代码

官方文档推荐

const store = createStore(
    reducer,
    applyMiddleware(thunk)
) // 创建数据存储仓库
复制代码

最后配置

但是这样我们便无法继续使用redux-devtools这个插件,所以我们应该利用增强函数这样配置

import { createStore , applyMiddleware ,compose } from 'redux' 
import reducer from './reducer'    
import thunk from 'redux-thunk'

const composeEnhancers =   window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
    window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({}):compose

const enhancer = composeEnhancers(applyMiddleware(thunk))

const store = createStore( reducer, enhancer) // 创建数据存储仓库
export default store   //暴露出去
复制代码

八.redux-thunk的使用

没有引入redux-thunk中间件前,我们请求数据只能在TodeList类组件中,有了redux-thunk,我们就可以在store将state传入reducer时对action做一些事情。

在actionCreators.js里编写业务逻辑

以前actionCreators.js都是定义好的action,根本没办法写业务逻辑,有了Redux-thunk之后,可以把TodoList.js中的componentDidMount业务逻辑放到这里来编写。也就是把向后台请求数据的代码放到actionCreators.js文件里。那我们需要引入axios,并写一个新的函数方法。(以前的action是对象,现在的action可以是函数了,这就是redux-thunk带来的好处)

import axios from 'axios'
...something...
export const getTodoList = () =>{
    return ()=>{
        axios.get('https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList').then((res)=>{
            const data = res.data
            console.log(data)
        })
    }
}
复制代码

现在我们需要执行这个方法,并在控制台查看结果,这时候可以修改TodoList.js文件中的componentDidMount代码。

//先引入getTodoList
import {getTodoList , changeInputAction , addItemAction ,deleteItemAction,getListAction} from './store/actionCreatores'
---something---
componentDidMount(){
    const action = getTodoList()
    store.dispatch(action)
}
复制代码

然后我们到浏览器的控制台中查看一下,看看是不是已经得到了后端传给我们的数据,如果一切正常,应该是可以得到。得到之后,我们继续走以前的Redux流程就可以了。

export const getTodoList = () =>{
    return (dispatch)=>{
        axios.get('https://www.easy-mock.com/mock/5cfcce489dc7c36bd6da2c99/xiaojiejie/getList').then((res)=>{
            const data = res.data
            const action = getListAction(data)
            dispatch(action)

        })
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享