Redux

JavaScript 纯函数

纯函数要符合以下两个特点:

  1. 确定的输入,一定会产生确定的输出
  2. 函数在执行过程中,不能产生副作用(在计算机科学中,副作用表示在执行一个函数时,除了返回函数值之外,还对调用函数产生了附加的影响,比如修改了全局变量修改参数或者改变外部的存储

纯函数的案例

slice :slice截取数组时不会对原数组进行任何操作,而是生成一个新的数组;
splice :splice截取数组, 会返回一个新的数组, 也会对原数组进行修改;
slice就是一个纯函数,不会修改数组本身,而splice函数不是一个纯函数;

纯函数的作用和优势

        写的时候 保证了函数的纯度,只是 单纯实现自己的业务逻辑 即可, 不需要关心传入的内容 是如何获得的或者 依赖其他的外部变量 是否已经发生了修改。

Redux的三大原则

  • 单一数据源

        整个应用程序的state只存储在一个 store 中,创建多个Store不利于数据的维护单一的数据源可以让整个应用程序的state变得方便维护、追踪、修改

  • State是只读的
        唯一修改 State 的方法一定是触发 action 不要试图在其他地方通过任何的方式来修改 State
  • 使用纯函数来执行修改
         通过reducer将 旧 state 和 actions 联系在一起 ,并且 返回一个新的 State

Redux的使用过程

  1. 使用store中的数据

提供方:

const { createStore } = require("redux");

// 创建一个对象,保存的状态
const initialState = {
  name: "why",
  counter: 100,
};

// 创建Store来存储这个state,创建store时必须创建reducer
function reducer(state = initialState, action) { // 初始值
  return state;
}

// 创建的store
const store = createStore(reducer); // reducer的返回值

module.exports = store;

使用方:

const store = require("./store")

console.log(store.getState()) // { name: 'why', counter: 100 }

2. 修改 store 中的数据

使用方修改数据:

// 通过action来修改state,通过dispatch来派发action

const nameAction = { type: "change_name", name: "kobe" }; // action
store.dispatch(nameAction); // 执行 reducer 函数

// 获取修改之后的 state 
console.log(store.getState()); // { name: 'kobe', counter: 100 }

提供者处理:

const { createStore } = require("redux");

// 初始化的数据
const initialState = {
  name: "why",
  counter: 100,
};

// 在这里处理
function reducer(state = initialState, action) {
  switch (action.type) {
    case "change_name":
      return { ...state, name: action.name }; // 不需要直接修改state
    case "add_number":
      return { ...state, counter: state.counter + action.num };
    default:
      return state;
  }
}

// 创建的store
const store = createStore(reducer);

module.exports = store;

3.  订阅 store 中的数据,store 中的数据改变之后打印

使用方:

const unsubscribe = store.subscribe(() => {
  console.log("订阅数据的变化:", store.getState())
})

// 修改 state 中的数据时
store.dispatch({ type: "change_name", name: "kobe" }) // 订阅数据的变化: { name: 'kobe', counter: 100 }

Redux结构划分

        将store、reducer、action、constants拆分成单个文件。
store/index.js
const { createStore } = require("redux");
const reducer = require("./reducer.js");

// 创建的store
const store = createStore(reducer);

module.exports = store;

store/reducer.js

const { ADD_NUMBER, CHANGE_NAME } = require("./constants")

// 初始化的数据
const initialState = {
  name: "why",
  counter: 100
}

function reducer(state = initialState, action) {
  switch(action.type) {
    case CHANGE_NAME:
      return { ...state, name: action.name }
    case ADD_NUMBER:
      return { ...state, counter: state.counter + action.num }
    default:
      return state
  }
}

module.exports = reducer

store/constants.js

const ADD_NUMBER = "add_number"
const CHANGE_NAME = "change_name"

module.exports = {
  ADD_NUMBER,
  CHANGE_NAME
}

store/actionCreators.js

const { ADD_NUMBER, CHANGE_NAME } = require("./constants")

const changeNameAction = (name) => ({
  type: CHANGE_NAME,
  name
})

const addNumberAction = (num) => ({
  type: ADD_NUMBER,
  num
})


module.exports = {
  changeNameAction,
  addNumberAction
}

使用方:

// 使用 actionCreators 中函数返回的 action 对象
const store = require("./store");
const { addNumberAction, changeNameAction } = require("./store/actionCreators");

store.dispatch(changeNameAction("kobe"));
store.dispatch(changeNameAction("lilei"));

Redux_第1张图片

redux融入react代码

案例:
  • Home组件:其中会展示当前的counter值,并且有一个+1和+5的按钮
  • Profile组件:其中会展示当前的counter值,并且有一个-1和-5的按钮

Redux_第2张图片

 代码:

home.jsx

import React, { PureComponent } from 'react'
import store from "../store"
import { addNumberAction } from '../store/actionCreators'

export class Home extends PureComponent {
  constructor() {
    super()

    this.state = {
      counter: store.getState().counter // 获取初始值
    }
  }

  componentDidMount() {
    store.subscribe(() => {
      const state = store.getState()
      this.setState({ counter: state.counter })
    })
  }

  addNumber(num) {
    store.dispatch(addNumberAction(num))
  }

  render() {
    const { counter } = this.state

    return (
      

Home Counter: {counter}

) } } export default Home
profile.jsx
import React, { PureComponent } from 'react'
import store from "../store"
import { subNumberAction } from '../store/actionCreators'

export class Profile extends PureComponent {
  constructor() {
    super()

    this.state = {
      counter: store.getState().counter
    }
  }

  componentDidMount() {
    store.subscribe(() => {
      const state = store.getState()
      this.setState({ counter: state.counter })
    })
  }

  subNumber(num) {
    store.dispatch(subNumberAction(num))
  }

  render() {
    const { counter } = this.state

    return (
      

Profile Counter: {counter}

) } } export default Profile

store / actionCreators.js

import * as actionTypes from "./constants"

export const addNumberAction = (num) => ({
  type: actionTypes.ADD_NUMBER,
  num
})

export const subNumberAction = (num) => ({
  type: actionTypes.SUB_NUMBER,
  num
})

store / constants.js

export const ADD_NUMBER = "add_number"
export const SUB_NUMBER = "sub_number"

store / reducer.js

import * as actionTypes from "./constants"

const initialState = {
  counter: 100
}

function reducer(state = initialState, action) {
  switch (action.type) {
    case actionTypes.ADD_NUMBER:
      return { ...state, counter: state.counter + action.num }
    case actionTypes.SUB_NUMBER:
      return { ...state, counter: state.counter - action.num }
    default:
      return state
  }
}

export default reducer

store / index.js

import { createStore } from "redux"
import reducer from "./reducer"

const store = createStore(reducer)

export default store

 react-redux

功能:将 redux 和页面建立连接

1. 安装react-redux: yarn add react-redux

2. 在 index.js 文件中使用 store,所有的页面都可以使用 store 了

import App from './App';
import { Provider } from "react-redux"
import store from "./store"

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
    
      
    
);

3. 页面中使用 connect 高阶函数,connect 高阶函数是需要传两个参数的。

about.jsx:

mapStateToProps  映射 state

import React, { PureComponent } from 'react'
import { connect } from "react-redux"

export class About extends PureComponent {
    render() {
        // 2. 映射过来的数据在 props 中获取
        const { counter, banners, recommends, userInfo } = this.props
    }
}

// 1. 将 store 中的数据映射过来
const mapStateToProps = (state) => ({
  counter: state.counter.counter,
  banners: state.home.banners,
  recommends: state.home.recommends,
  userInfo: state.user.userInfo
})

export default connect(mapStateToProps)(About)

mapDispatchToProps 映射 action 

页面中执行一个操作的时候,函数中是要调用 dispatch 函数,操作过多,应该要简化。

about.jsx

import React, { PureComponent } from "react";
import { connect } from "react-redux";
import { addNumberAction, subNumberAction } from "../store/counter";

export class About extends PureComponent {
  calcNumber(num, isAdd) {
    if (isAdd) {
      this.props.addNumber(num);
    } else {
      this.props.subNumber(num);
    }
  }

  render() {
    const { banners, userInfo } = this.props;

    return (
      

nickname: {userInfo.nickname}

轮播图数据:

    {banners.map((item, index) => { return
  • {item.title}
  • ; })}
); } } const mapStateToProps = (state) => ({ banners: state.home.banners, userInfo: state.user.userInfo, }); const mapDispatchToProps = (dispatch) => ({ addNumber(num) { dispatch(addNumberAction(num)); }, subNumber(num) { dispatch(subNumberAction(num)); }, }); export default connect(mapStateToProps, mapDispatchToProps)(About);

Redux 异步操作

网络请求到的数据属于状态管理的一部分,也交给redux来管理。

Redux_第3张图片

        dispatch 函数中需要传递一个对象,并且在 dispatch 是同步执行的,不能执行异步操作,需要将 dispatch 函数进行增强,在这里使用 redux-thunk 作为中间件增强 dispatch 函数。

1. 安装redux-thunk: yarn add redux-thunk
2.  在创建store时传入应用了 middleware 的 enhance 函数(增强函数)
  • store / index.js
import { createStore, applyMiddleware } from "redux"
import thunk from "redux-thunk"
import reducer from "./reducer"

const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))

export default store

3. 这时候可以定义返回值是函数的 action,这个函数在 dispatch 之后执行。

页面代码:

import React, { PureComponent } from 'react'
import { connect } from "react-redux"
import { fetchHomeMultidataAction } from "../store/actionCreators"

export class Category extends PureComponent {
  componentDidMount() {
    // 去请求数据了
    this.props.fetchHomeMultidata()
  }

  render() {
    return (
      

Category Page: {this.props.counter}

) } } const mapStateToProps = (state) => ({ counter: state.counter }) const mapDispatchToProps = (dispatch) => ({ fetchHomeMultidata() { dispatch(fetchHomeMultidataAction()) // action 是函数类型 } }) export default connect(mapStateToProps, mapDispatchToProps )(Category)

actionCreators.js

import axios from "axios";

export const changeBannersAction = (banners) => ({
  type: actionTypes.CHANGE_BANNERS,
  banners,
});

export const changeRecommendsAction = (recommends) => ({
  type: actionTypes.CHANGE_RECOMMENDS,
  recommends,
});

export const fetchHomeMultidataAction = () => {  // 这里的 action 返回值是函数
  return function (dispatch, getState) {
    axios.get("http://123.207.32.32:8000/home/multidata").then((res) => {
      const banners = res.data.data.banner.list;
      const recommends = res.data.data.recommend.list;
      // 回来数据之后,调用原来的action 
      dispatch(changeBannersAction(banners));  
      dispatch(changeRecommendsAction(recommends));
    });
  };
};

redux-devtools

第一步:在对应的浏览器中安装相关的插件
第二步:在redux中继承devtools的中间件
import { createStore, applyMiddleware, compose } from "redux"
import thunk from "redux-thunk"
import reducer from "./reducer"



// redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))

export default store

Reducer代码拆分

combineReducers 函数
store / index.js
import { createStore, applyMiddleware, compose, combineReducers } from "redux"
import thunk from "redux-thunk"

import counterReducer from "./counter"
import homeReducer from "./home"
import userReducer from "./user"

// 将两个reducer合并在一起
const reducer = combineReducers({
  counter: counterReducer,
  home: homeReducer,
  user: userReducer
})
// redux-devtools
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true}) || compose;
const store = createStore(reducer, composeEnhancers(applyMiddleware(thunk)))

export default store

页面中使用:

  constructor() {
    super()
    this.state = {
      counter: store.getState().counter.counter
    }
  }

你可能感兴趣的:(react,前端,javascript,开发语言)