React Redux一次简单实现

132 阅读4分钟

Redux

  • Redux是一个数据管理框架,经常和React搭配使用
  • 由于React写大型复杂应用易遇到困难(代码结构、组件间通信问题),2014年Facebook提出Flux架构概念。
  • 2015年,Redux出现,将Flux函数式编程结合在一起
  • 最大优势:自带规范,将数据管理定于为三个核心概念
  1. store // 数据仓库 一个数据就是一个房间
  2. reducer // 组合仓库里面各个房间的工具
  3. action // 给房间发放指令过程,接收action发送过来的指令,然后更新房间数据

安装

 npm install redux

一般在react使用redux的话要安装react附加包

npm install react-redux

npm install --save-dev redux-devtools  // 开发者工具可以不装

store

Store 就是把它们联系到一起的对象。Store 有以下职责:

  • 维持应用的 state;
  • 提供 getState() 方法获取 state;
  • 提供 dispatch(action) 方法更新 state;
  • 通过 subscribe(listener) 注册监听器;
  • 通过 subscribe(listener) 返回的函数注销监听器。

再次强调一下 Redux 应用只有一个单一的 store。当需要拆分数据处理逻辑时,你应该使用 reducer 组合 而不是创建多个 store。

reducer

Reducers 指定了应用状态的变化如何响应 actions 并发送到 store 的,记住 actions 只是描述了有事情发生了这一事实,并没有描述应用如何更新 state。

action

Action 是把数据从应用(译者注:这里之所以不叫 view 是因为这些数据有可能是服务器响应,用户输入或其它非 view 的数据 )传到 store 的有效载荷。它是 store 数据的唯一来源。一般来说你会通过 store.dispatch() 将 action 传到 store。

image

以下为redux的一个简单实现

目录为:


    ├── actions // actions 目录
    |   ├── friend.js // 单个action文件
    ├── reducers // reducers 目录
    |   ├── friend.js  // 单个reducer文件
    |   ├── index.js // redux reducer文件  在这里处理所有reducer文件,把单个reducer合并然后export
    ├── store // store目录
    |   ├── index.js  // redux store文件
    ├── views // 组件视图目录
    |   ├── friend
    |       ├── index.js // friend组件文件
    ├── App.js  // App组件文件
    ├── index.js // 项目入口文件

文件明细

在项目入口文件 index.js要引入store 并且定义全局的store

import React from "react";
import { render } from "react-dom";
import { Provider } from "react-redux"; // 引入Provider组件
import { store } from "./store";

import App from "./App";

render(
  <Provider store={store}> // 传入store
    <App></App>
  </Provider>,
  document.getElementById("root")
);

store/index.js文件下定义store

import { applyMiddleware, compose, createStore } from "redux";
import rootReducer from "../reducers"; // 引入合并后reducer对象

const middlewares = [];
// 开发环境使用redux报错信息,可不用
if (process.env.NODE_ENV === "development") {
  const { logger } = require("redux-logger");
  middlewares.push(logger);
}
export const store = compose(applyMiddleware(...middlewares))(createStore)(rootReducer);
 // 通过redux里的方法构造store

因为在store/index.js引入了reducers,接下来我们看一下reducers目录下的文件内容

reducers/index.js

import { combineReducers } from "redux";//引入combineReducers方法,将所有单个reducer合并

import friend from "./friend"; //引入friend reducer文件

export default combineReducers({
  friend
});

reducers/friend.js

import { ADD_LIST, DEL_ITEM } from '../actions/friend'//引入action文件,获取到action里面定义的动作

const initialState = {
  list: [
    { name: 'lizi', id: 0 },
    { name: 'vue', id: 1 },
    { name: 'react', id: 2 }
  ]
} // 定义初始化的 state数据
// 向外抛出一个方法函数 
export default (state = initialState, action = {}) => {
  const { type, data } = action
  let List = state.list
  switch (type) { // 监听dispatch触发的type,并执行相对于的方法,进行对state数据的操作,然后返回修改后的state,更新到对应的组件。
    case ADD_LIST:
      List.push(data)
      return Object.assign({}, state, { list: [...List] }) // 这边List要解构,不然和state.list的内存地址是一样的,js认为数据没改变
    case DEL_ITEM:
      List = List.filter(item => item.id != data)
      return Object.assign({}, state, { list: [...List] })
    default:
      return state
  }
}

接下来我们看一下actions/friend.js里面做了什么东西。

import { store } from "../store";// 引入store对象

export const ADD_LIST = "ADD_LIST"; // 定义导出方法名
export const DEL_ITEM = "DEL_ITEM";// 定义导出方法名

export function addList(data) { //定义导出对应的方法,提供在组件内调用
  const dispatch = store.dispatch;// 获取到store.dispatch
  setTimeout(() => {
    dispatch({ type: ADD_LIST, data }); // 触发reducer里面的对应方法
  }, 1000);// 异步更新
  // dispatch({ type: ADD_LIST, data }); // 同步更新
}
export function delItem(data) {
  const dispatch = store.dispatch;
  dispatch({ type: DEL_ITEM, data });
}

我们回到App.js组件内,App.js只是简单的引入views/friend.js文件

import React from 'react'
import Friend from './views/friend'

export default class App extends React.Component {
    render() {
        return (
            <div>
                <Friend></Friend>// 这边并没有给Friend组件注入props,我们看一下如何在friend组件使用全局的state
            </div>
        )
    }
}

最后,我们看一下views/friend.js文件,看看如何在组件内使用全局state的值,并且调用已经定义的修改方法

import React from 'react'
import { connect } from 'react-redux' // 在react-redux模块内引入connect方法
import { addList, delItem } from '../../actions/friend'// 引入对应的action方法

class friend extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            value: ''
        }
    }
    render() {
        const {
            value
        } = this.state
        const {
            friendList
        } = this.props // 通过connect获取到state.friend.list
        return (
            <div>
                {friendList.map((item, index) =>
                    <div key={item.id}>
                        <p>{item.name}</p>
                        <button onClick={() => {
                            delItem(item.id) 
                        }}>del</button>
                        // button方法触发action里面的delItem方法,修改数据
                    </div>
                )}
                <input value={value} onChange={e => {
                    this.setState({ value: e.target.value })
                }} />
                <button onClick={(e) => {
                    addList({ name: value, id: friendList.length })
                    this.setState({ value: '' })
                }}>add</button>
                // button方法触发action里面的addList方法,修改数据
            </div>
        )
    }
}

export default connect((state) => {
    return {
        friendList: state.friend.list
    }
})(friend) // 通过connect方法,获取到state的值,并且赋值到组件的props.friendList

以上为redux的一个简单实现过程,如果需要添加多个reducer,则需要在reducers目录下新建对应的reducer,并且在reducers/index.js中进行合并,即可在页面上使用