【前端学习】React学习笔记-redux、纯函数、打包

8 篇文章 0 订阅

跟着尚硅谷的天禹老师学习React
看视频可以直接点击 b站视频地址

简介

redux是什么
  1. redux是一个专门用于状态管理的JS库(不是react插件库
  2. 它可以用在react,angular,vue等项目中,但基本与react配合使用。
  3. 作用:集中式管理react应用中多个组件共享的状态
redux在什么情况下使用
  1. 某个组件的状态,需要让其他组件可以随时拿到(共享)
  2. 一个组件需要改变另一个组建的状态(通信)
  3. 总体原则:能不用就不用,除非比较难以实现的时候再用。

redux的三个核心概念

在这里插入图片描述

  1. action
    • 动作的对象
    • 包含两个属性:
      • type:标识属性,值为一个字符串,唯一,必填
      • data:数据属性,值类型任意,可选
    • 例子:{type:‘ADD_STUDENT’,data:{name:‘tom’,age:18}}
  2. reducer
    • 用于初始化状态、加工状态
    • 加工时,根据旧的state和action,产生新的state的纯函数
  3. store
    • 将state、action、reducer联系在一起的对象
    • 如何得到这个对象?
      • import {createStore} from ‘redux’
      • import reducer from ‘./reducers’
      • const store = createStore(reducer)
    • 此对象的功能?
      • getState():得到state
      • dispatch(action):分发action,触发reducer调用,产生新的state
      • subscribe(listener):注册监听,当产生了新的state时,自动调用

redux的核心API

redux编写应用:加减计算器

目录结构

在这里插入图片描述

代码

Count.jsx

/* Count.jsx */
import React, { Component } from "react";
// 引入store,用于获取store的状态
import store from "../../redux/store";
export default class Count extends Component {
  state = {
    count: 0,
  };
  componentDidMount() {
    store.subscribe(() => {
      this.setState({});
    });
  }
  increment = () => {
    const { value } = this.selectNumber;
    store.dispatch({ type: "increment", data: value * 1 });
  };
  decrement = () => {
    const { value } = this.selectNumber;
    store.dispatch({ type: "decrement", data: value * 1 });
  };
  incrementOdd = () => {
    const { value } = this.selectNumber;
    const count = store.getState();
    const result = count + value * 1;
    if (result % 2 !== 0) {
      store.dispatch({ type: "increment", data: value * 1 });
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    setTimeout(() => {
      store.dispatch({ type: "increment", data: value * 1 });
    }, 500);
  };
  render() {
    return (
      <div>
        <h1>当前求和为:{store.getState()}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <br></br>
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
        <button onClick={this.incrementOdd}>求和为奇数再加</button>
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

counter_reducer.js

/* counter_reducer.js */
/** 用于创建一个为Count组件服务的reducer
 *  reducer会接收两个参数一个是原状态,一个是动作对象
 */
const initState = 0;
export default function (preState = initState, action) {
    console.log(preState,action)
  // 从action对象中获取:type、data
  const { type, data } = action;
  // 根据type决定如何加工数据
  switch (type) {
    case "increment":
      return preState + data;
    case "decrement":
      return preState - data;
    default:
      // 初始化
      return preState;
  }
}

store.js

/** 该文件专门用于暴露一个store对象 */
// 引入createStore
import { createStore } from "redux";
import countReducer from "./count_reducer";

export default createStore(countReducer);
一个小技巧

如果在componentDidMount生命周期钩子中监听store的变化,一旦这个状态用得很多,我们可以这样做:直接在index.js中监听这个状态的变化。因为有diffing的原因所以这个性能损耗不会太高。

store.subscribe(() => {
  ReactDOM.render(<App />, document.getElementById("root"));
});

小结
  1. 使用createStore函数来创建一个store
  2. 注意创建createStore时要传入一个reducer
  3. reducer本质是一个纯函数,接收preState和action,返回加工后的状态。
  4. reducer有两个作用:初始化状态,加工状态
  5. reducer被第一调用时是store自动触发的,传递的preState是undefined,action是一个包含“@@redux/INITg.b.n.m.h.7”字符串的对象
  6. redux只负责管理状态,至于状态的改变驱动着页面的展示,要靠我们自己写。
完整的redux使用

在这里插入图片描述
增加了constant和action两个部分,constant为了统一管理type字符串,action是为了创建action时更简洁。

constant.js

/* constant.js */
/**
 * 该模块适用于定义action对象中type类型的常量值 
 * 单纯是为了防止常量改错,并且防止重新命名时难以维护
 */
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

count_action.js

/* count_action.js */
/**
 * 该文件专门为Count组件生成action对象
 */
import { DECREMENT, INCREMENT } from "./constant";
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });

count_reducer.js

/* count_reducer.js */
/** 用于创建一个为Count组件服务的reducer
 *  reducer会接收两个参数一个是原状态,一个是动作对象
 */
import {INCREMENT,DECREMENT} from './constant'
const initState = 0;
export default function (preState = initState, action) {
  // 从action对象中获取:type、data
  const { type, data } = action;
  // 根据type决定如何加工数据
  switch (type) {
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      // 初始化
      return preState;
  }
}

store.js

/* store.js */
/** 该文件专门用于暴露一个store对象 */
// 引入createStore
import { createStore } from "redux";
import countReducer from "./count_reducer";

export default createStore(countReducer);

异步action

action分为两种类型,一种是Object类型的,称为同步action;一种是function类型的,称为异步action。

redux-thunk中间件

由于store只期待接收一个一般的object,所以必须引入中间件来处理这个问题。

代码

修改后的store.js

/* 修改后的store.js */
/** 该文件专门用于暴露一个store对象 */
// 引入createStore
import { createStore,applyMiddleware } from "redux";
// 引入为store服务的reducer
import countReducer from "./count_reducer";
// 引入为redux-thunk,用于支持异步action、
import thunk from 'redux-thunk'
// 暴露store
export default createStore(countReducer,applyMiddleware(thunk));

修改后的count_action.js

/* 修改后的count_action.js */
/**
 * 该文件专门为Count组件生成action对象
 */
import { DECREMENT, INCREMENT } from "./constant";

// 同步action就是指action的值为Object
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });
// 异步action就是指action的值为函数,异步action一般都会调用同步action,异步action一般不是必须使用的
export const createIncrementAsyncAction = (data, delay) => {
  return (dispatch) => { // store会传入一个dispatch参数
    setTimeout(() => {
      dispatch(createIncrementAction(data));
    }, delay);
  };
};

可以看到我们在createIncrementAsyncAction中声明了两个形式参数,所以在调用方法的时候应该这样传:

store.dispatch(createIncrementAsyncAction(value * 1,500));
小结
  1. 明确:延迟的动作不想交给组件本身,想要交给action
  2. 何使需要异步action:想要对状态进行操作,但是具体的数据要靠异步任务返回
  3. 具体编码
    • npm install redux-thunk,并配置在store中
    • 创建action的函数不再返回一般对象,而是一个函数,该函数中写异步任务
    • 异步任务有结果后,分发一个同步的action去真正操作数据
  4. 备注:异步action不是必须要写的。完全可以自己等待异步任务的结果再去分发同步action

对react-redux的理解

redux本身是另一个团队的研发产品,因为很多react开发者都使用redux做状态管理,所以facebook也出了一个react-redux库。
在这里插入图片描述

  1. 所有的UI组件都应该包裹一个容器组件,它们是父子关系
  2. 容器组件是真正跟redux打交道的,里面可以随意地使用redux的api
  3. UI组件中不能使用任何redux的api
  4. 容器组件会传给UI组件:
    • redux中所保存的状态
    • 用于操作状态的方法
  5. 备注:容器给UI传递的状态和操作状态的方法,均通过props传递。

react似乎不希望开发者直接在UI组件中修改redux中的值。

使用react-redux完成计算器案例
目录

在这里插入图片描述

代码

App.js

/* App.js */
import React, { Component } from "react";
import Count from './containers/Count'
import store from "./redux/store";
export default class App extends Component {
  render() {
    return (
      <div>
        {/* 必须通过props的方式传入store */}
        <Count store={store}/>
      </div>
    );
  }
}

constant.js

/* constant.js */
/**
 * 该模块适用于定义action对象中type类型的常量值 
 * 单纯是为了防止常量改错,并且防止重新命名时难以维护
 */
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'

count_action.js

/* count_action.js */
/**
 * 该文件专门为Count组件生成action对象
 */
import { DECREMENT, INCREMENT } from "./constant";

// 同步action就是指action的值为Object
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });
// 异步action就是指action的值为函数,异步action一般都会调用同步action,异步action一般不是必须使用的
export const createIncrementAsyncAction = (data, delay) => {
  return (dispatch) => { // store会传入一个dispatch参数
    setTimeout(() => {
      dispatch(createIncrementAction(data));
    }, delay);
  };
};

count_reducer.js

/* count_reducer.js */
/** 用于创建一个为Count组件服务的reducer
 *  reducer会接收两个参数一个是原状态,一个是动作对象
 */
import {INCREMENT,DECREMENT} from './constant'
const initState = 0;
export default function (preState = initState, action) {
  // 从action对象中获取:type、data
  const { type, data } = action;
  // 根据type决定如何加工数据
  switch (type) {
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      // 初始化
      return preState;
  }
}

store.js

/* store.js */
/** 该文件专门用于暴露一个store对象 */
// 引入createStore
import { createStore,applyMiddleware } from "redux";
// 引入为store服务的reducer
import countReducer from "./count_reducer";
// 引入为redux-thunk,用于支持异步action、
import thunk from 'redux-thunk'
// 暴露store
export default createStore(countReducer,applyMiddleware(thunk));

UI Count

/* UI Count */
import React, { Component } from "react";

export default class Count extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.jia(value * 1);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.jian(value * 1);
  };
  incrementOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.jia(value * 1);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.jiaAsync(value*1,500)
  };
  render() {
    // console.log(this.props);
    return (
      <div>
        <h1>当前求和为:{this.props.count}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <br></br>
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
        <button onClick={this.incrementOdd}>求和为奇数再加</button>
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}

容器 Count

/* 容器Count */
// 引入Count的UI组件
import CountUI from "../../components/Count";
// 引connect用于连接UI组件与redux
import { connect } from "react-redux";
// 引入action
import { createIncrementAction ,createDecrementAction,createIncrementAsyncAction} from "../../redux/count_action";
/**
 * 1. mapStateToProps函数的返回的是一个对象:
 * 2. 对象的key作为传递给UI组件的props的key,value作为传递给UI组件的props的value
 * 3. mapStateToProps用于传递状态
*/
const mapStateToProps = (state) => {
  return {
    count: state,
  };
};
/**
 * 1. mapStateToProps函数的返回的是一个对象:
 * 2. 对象的key作为传递给UI组件的props的key,value作为传递给UI组件的props的value
 * 3. mapStateToProps用于传递操作状态的方法
*/
const mapDispatchToProps = (dispatch) => {
  return {
    // 通知redux执行加法
    jia: (number) => dispatch(createIncrementAction(number)),
    jian: (number) => dispatch(createDecrementAction(number)),
    jiaAsync: (number,delay) => dispatch(createIncrementAsyncAction(number,delay)),
  
};
};
// 使用connect创建并暴露一个Count的容器组件
export default connect(mapStateToProps, mapDispatchToProps)(CountUI);
react-redux的基本使用小结
  1. 明确两个概念:
    • UI组件:不能使用任何redux的api,只负责页面的呈现和交互
    • 容器组件:负责和redux通信,将结果交给UI组件
  2. 如何创建一个容器组件:使用react-redux的connect函数
    • mapStateToprops:映射状态,返回值是一个对象
    • mapDispatchToProps:映射操作状态的方法,返回值是一个对象
  3. 备注:容器组件中的store式靠props传进去的,而不是直接在容器组件中直接引入
优化1-MapDispatchToProps的简写

对于MapDispatchToProps直接传函数名即可。redux会自动分发这个action。

/* ( containers/Count.jsx ) */
// 引入Count的UI组件
import CountUI from "../../components/Count";
// 引connect用于连接UI组件与redux
import { connect } from "react-redux";
// 引入action
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";
// 使用connect创建并暴露一个Count的容器组件
export default connect((state) => ({ count: state }), {
  jia: createIncrementAction,
  jian: createDecrementAction,
  jiaAsync: createIncrementAsyncAction,
})(CountUI);

所以MapDispatchToProps可以传入两种类型的参数:

  1. 函数,有一个dispatch的参数,要手动分发。
  2. 对象,给出创建action的函数名,redux自动分发。
优化2-connect对于redux的优化

不需要手动监听状态变化然后再渲染组件,react-redux正在connect的时候已经做完这部分的工作。

优化3-Provider组件的使用

Provider组件和connect方法可以自动地分析react代码中需要传入store的组件,然后自动注入。

/* index.js */
// 引入React核心库
import React from "react";
// 引入ReactDOM
import ReactDOM from "react-dom";
// 引入App组件
import App from "./App";
import store from "./redux/store";
import { Provider } from "react-redux";
// 渲染App到页面
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

App.js

/* App.js */
import React, { Component } from "react";
import Count from './containers/Count'
export default class App extends Component {
  render() {
    return (
      <div>
        {/* 这里已经不需要通过Props传入store了 */}
        <Count/>
      </div>
    );
  }
}
优化4-整合容器组件和UI组件

既然只需要渲染一下那个容器组件即可,那么可以直接将UI组件和容器组件放在一个文件内。默认暴露容器组件,分别暴露UI组件。现在可以直接把components文件夹给删掉了。

import React, { Component } from "react"
// 引connect用于连接UI组件与redux
import { connect } from "react-redux";
// 引入action
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/count_action";

// 定义UI组件
export class CountUI extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.jia(value * 1);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.jian(value * 1);
  };
  incrementOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.jia(value * 1);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.jiaAsync(value*1,500)
  };
  render() {
    // console.log(this.props);
    return (
      <div>
        <h1>当前求和为:{this.props.count}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <br></br>
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
        <button onClick={this.incrementOdd}>求和为奇数再加</button>
        <button onClick={this.incrementAsync}>异步加</button>
      </div>
    );
  }
}
// 使用connect创建并暴露一个Count的容器组件
export default connect((state) => ({ count: state }), {
  jia: createIncrementAction,
  jian: createDecrementAction,
  jiaAsync: createIncrementAsyncAction,
})(CountUI);
优化小结
  1. 容器组件和UI组件整合成一个文件
  2. 无需自己给容器组件传入store
  3. 使用了react-redux后也不用自己再检测redux中状态的改变,容器组件可以自动地完成这个工作
  4. mapDispatchToProps可以简单地写成一个对象
  5. 一个组件和redux”打交道“要经过哪几步?
    • 定义好UI组件(不暴露)
    • 引入connect生成一个容器组件,并暴露,写法如下:
      connect(
      state => ({key:value}), // 映射状态
      {key:xxxxxAction} // 映射操作状态的方法
      )(UI组件)
    • 在UI组件中通过this.props.xxxxxx读取和操作状态
实现数据共享

模拟了真实的开发场景,多个组件同时操作一个状态。

功能需求

在这里插入图片描述

目录

在这里插入图片描述

代码

index.js
注意这里使用Provider组件包裹了App,节省了手动注入state的操作。

// 引入React核心库
import React from "react";
// 引入ReactDOM
import ReactDOM from "react-dom";
// 引入App组件
import App from "./App";
import store from "./redux/store";
import { Provider } from "react-redux";
// 渲染App到页面
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);

App.js
新增了一个Person组件

import React, { Component } from "react";
import Count from './containers/Count'
import Person from "./containers/Person";
export default class App extends Component {
  render() {
    return (
      <div>
        {/* 必须通过props的方式传入store */}
        <Count/>
        <hr />
        <Person/>
      </div>
    );
  }
}

store.js
这里使用thunk来完成异步action的操作,使用combineReducer完成reducers的合并(汇总)。并且可以在state中以count和persons为键保存这两个状态。

/** 该文件专门用于暴露一个store对象 */
// 引入createStore
import { createStore, applyMiddleware, combineReducers } from "redux";
// 引入为store服务的reducer
import countReducer from "./reducers/count";
// 引入为person服务的reducer
import personReducer from "./reducers/person";
// 引入为redux-thunk,用于支持异步action、
import thunk from "redux-thunk";
// 汇总所有reducer
const allReducer = combineReducers({
  count: countReducer,
  persons: personReducer,
});
// 暴露store
export default createStore(allReducer, applyMiddleware(thunk));

constant.js
暴露一些常量,防止程序员写错。

/**
 * 该模块适用于定义action对象中type类型的常量值 
 * 单纯是为了防止常量改错,并且防止重新命名时难以维护
 */
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
export const ADD_PERSON = "add_person"

actions/count.js
向Count组件提供生成action的方法

/**
 * 该文件专门为Count组件生成action对象
 */
import { DECREMENT, INCREMENT } from "../constant";
// 同步action就是指action的值为Object
export const createIncrementAction = (data) => ({ type: INCREMENT, data });
export const createDecrementAction = (data) => ({ type: DECREMENT, data });
// 异步action就是指action的值为函数,异步action一般都会调用同步action,异步action一般不是必须使用的
export const createIncrementAsyncAction = (data, delay) => {
  return (dispatch) => { // store会传入一个dispatch参数
    setTimeout(() => {
      dispatch(createIncrementAction(data));
    }, delay);
  };
};

actions/person.js

import { ADD_PERSON } from "../constant";
// 创建增加一个人的action
export const createAddPersonAction = (person) => ({
  type: ADD_PERSON,
  data: person,
});

reducers/count.js

/** 用于创建一个为Count组件服务的reducer
 *  reducer会接收两个参数一个是原状态,一个是动作对象
 */
import {INCREMENT,DECREMENT} from '../constant'
const initState = 0;
export default function (preState = initState, action) {
  // 从action对象中获取:type、data
  const { type, data } = action;
  // 根据type决定如何加工数据
  switch (type) {
    case INCREMENT:
      return preState + data;
    case DECREMENT:
      return preState - data;
    default:
      // 初始化
      return preState;
  }
}

reducers/person.js

import { ADD_PERSON } from "../constant";

// 初始化人的列表
const initState = [{ name: "tom", id: "001", age: 18 }];
export default function personReducer(preState = initState, action) {
  const { type, data } = action;
  switch (type) {
    case ADD_PERSON:
      return [data, ...preState];
    default:
      return preState;
  }
}

Count.jsx
Count的UI组件和容器组件,注意这里的“jia”、“jian”就是传给UI组件的操作状态的方法。count和persons就是传入组件的状态。

import React, { Component } from "react";
// 引connect用于连接UI组件与redux
import { connect } from "react-redux";
// 引入action
import {
  createIncrementAction,
  createDecrementAction,
  createIncrementAsyncAction,
} from "../../redux/actions/count";

// 定义UI组件
export class CountUI extends Component {
  increment = () => {
    const { value } = this.selectNumber;
    this.props.jia(value * 1);
  };
  decrement = () => {
    const { value } = this.selectNumber;
    this.props.jian(value * 1);
  };
  incrementOdd = () => {
    const { value } = this.selectNumber;
    if (this.props.count % 2 !== 0) {
      this.props.jia(value * 1);
    }
  };
  incrementAsync = () => {
    const { value } = this.selectNumber;
    this.props.jiaAsync(value * 1, 500);
  };
  render() {
    return (
      <div>
        <h1>我是Count组件</h1>
        <h1>当前求和为:{this.props.count}</h1>
        <select ref={(c) => (this.selectNumber = c)}>
          <option value="1">1</option>
          <option value="2">2</option>
          <option value="3">3</option>
        </select>
        <br></br>
        <button onClick={this.increment}>+</button>
        <button onClick={this.decrement}>-</button>
        <button onClick={this.incrementOdd}>求和为奇数再加</button>
        <button onClick={this.incrementAsync}>异步加</button>
        <h2>下方人数和为:{this.props.persons.length}</h2>
      </div>
    );
  }
}

// 使用connect创建并暴露一个Count的容器组件
export default connect(
  (state) => ({ count: state.count, persons: state.persons }),
  {
    jia: createIncrementAction,
    jian: createDecrementAction,
    jiaAsync: createIncrementAsyncAction,
  }
)(CountUI);

Person.jsx
Person的UI组件和容器组件,注意这里将persons和count包裹成了一个名为state的prop传入了组件。

import React, { Component } from "react";
import { nanoid } from "nanoid";
import { connect } from "react-redux";
import { createAddPersonAction } from "../../redux/actions/person";
export class PersonUI extends Component {
  addPerson = () => {
    const name = this.nameNode.value;
    const age = this.ageNode.value;
    const id = nanoid();
    const person = { name, age, id };
    this.props.addPerson(person)
  };
  render() {
    console.log(this.props)
    return (
      <div>
        <h1>我是Person组件</h1>
        <input
          ref={(c) => (this.nameNode = c)}
          type="text"
          placeholder="输入名字"
        ></input>
        <input
          ref={(c) => (this.ageNode = c)}
          type="text"
          placeholder="输入年龄"
        ></input>
        <button onClick={this.addPerson}>添加</button>
        <ul>
          {this.props.state.persons.map((person) => {
            return (
              <li key={person.id}>
                姓名:{person.name}---年龄:{person.age}
              </li>
            );
          })}
        </ul>
        <h2>上方求和为{this.props.state.count}</h2>
      </div>
    );
  }
}
export default connect((state) => ({ state:state }), {
  addPerson: createAddPersonAction,
})(PersonUI);
小结
  1. Person的reducer和Count的reducer要使用combineReducers进行合并,合并后的总状态是一个对象。
  2. 交给store的总是reducer,最后注意在组件中取出状态的时候,记得要“取到位”。
小的修改

1、在reducers文件夹下新建index.js文件汇总所有的reducers

/* reducers/index.js */
import count from './count'
import person from './person'
import { combineReducers } from 'redux'
const allReducer = combineReducers({count,person})  
export default allReducer

2、将actions中的文件,将createXxxxxx都变成简单的意思,比如createIncrementAction改成increment,这样会减少代码量。
actions/count.js

/* actions/count.js */
/**
 * 该文件专门为Count组件生成action对象
 */
import { DECREMENT, INCREMENT } from "../constant";
// 同步action就是指action的值为Object
export const increment = (data) => ({ type: INCREMENT, data });
export const decrement = (data) => ({ type: DECREMENT, data });
// 异步action就是指action的值为函数,异步action一般都会调用同步action,异步action一般不是必须使用的
export const createIncrementAsyncAction = (data, delay) => {
  return (dispatch) => { // store会传入一个dispatch参数
    setTimeout(() => {
      dispatch(increment(data));
    }, delay);
  };
};

Count.jsx

/* Count.jsx */
export default connect(
  (state) => ({ count: state.count, persons: state.persons }),
  {
    increment,
    decrement,
    incrementAsync,
  }
)(CountUI);

纯函数

在写上一个案例时,reducers/person.js中要写成一个返回新数组的返回值。

return [data, ...preState];

如果我们使用preState.unshift(data),发现状态的改变并不能被检测到,这是因为react-redux仅对数据进行了一个“浅比较”,发现数组的引用地址并没有变,被认为没有进行数据的更新。所以必须使用纯函数的思想来处理。
因此redux规定:reducer必须为一个纯函数。
纯函数(Prue function)具有以下特点:

  1. 纯函数每一次调用时传入同样的参数,返回的都是同样的结果;它不会改变参数的值,也不会改变外部变量的值;它不会依赖于外部的变量,仅依赖于你传入的参数;
  2. 纯函数没有其他副作用(side effect),例如不能有I/O操作、网络请求。
  3. 如果你每次传入的参数一样,但是返回的结果不一样,则不是一个纯函数(也就是说只要输入相同,那输出一定也相同)

纯函数更像是数学中的函数。纯函数是函数式编程的基础。

redux开发者工具

1、首先需要在chrome插件市场下载redux-devtools,如果没有外网条件可以去一些类似极简插件的网站下载,使用开发者模式安装。
2、安装一个名为redux-devtools-extension的库
npm install redux-devtools-extension
3、在redux/store.js中引入composeWithDevTools
然后在createStore的第二个参数里再套一层

import { composeWithDevTools } from "redux-devtools-extension";
export default createStore(allReducer,composeWithDevTools(applyMiddleware(thunk)) );

4、检查一下chrome开发者工具里是否有redux那个标签,如果有并且能够使用就是安装成功了。
在这里插入图片描述

打包项目

1、npm run build 或 yarn build,当然这都是根据package.json中scripts的值来决定的。

"scripts": {
    "start": "react-app-rewired start",
    "build": "react-app-rewired  build",
    "test": "react-app-rewired  test",
    "eject": "react-scripts eject"
  },

2、打包完成后会有一个build文件夹,这个就是最终产物。
在这里插入图片描述

部署项目

1、我们可以借用一个简单的库:serve,来简单部署一下这个打包后的文件。使用npm【全局安装】serve库
npm install serve -g
2、进入build目录下,输入serve指令。入过不希望进入在build目录,可以在项目根目录下 serve build。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值