redux是专门做状态管理的独立第3方库,对应用中状态进行集中式管理,与react-redux, redux-thunk 等插件配置使用
react-redux: 主要用于简化react应用中使用redux(react组件和redux进行通信的时候,代码耦合度比较高)提供了两个API
- Provider(这是一个组件),接收一个store
<Provider store={store}> <App /> </Provider> - connect(将react组件和redux连接起来,进行通信)
Redux理解
什么? redux是专门做状态管理的独立第3方库,不是react插件,但一般都用在react项目中
作用? 对应用中状态进行集中式管理(写/读)
开发: 与react-redux, redux-thunk 等插件配置使用
Redux三个核心的概念
action:
由调用redux仓库的事件发出,可以想象为有人借书,或者管理员往图书馆里添加书,这些操作都会告诉管理员
默认是对象(同步action),{type:'xxx',data:value} 需要通过对应的actionCreator产生
它的值也可以是函数(异步action),需要引入redux-thunk才可以
reducer
借书或者还书的行为,这些动作如何改变图书馆藏书的状态,就需要Reducer来计算,同时反馈给store,这个时候得到的就是新的仓库状态了
根据老的state和指定的action,返回一个新的state
不能修改老的state
createStore(), applyMiddleware(), combineReducers() 常用的/核心的
store
仓库,存储数据的地方,可以把他比喻为一个图书管理员,能提供的是当前藏书的状态
redux最核心的管理对象
内部管理者:state和reducer
提供方法: getState()读状态数据, dispatch()更新状态数据, subscribe() 监视状态数据的改变
redux 基本使用教程
-
搭建项目,使项目能跑起来
-
安装redux
npm install --save redux -
在
src->redux新建store.jsactions.jsreducer.jsaction-types.js-
store.jsredux最核心的管理对象:store-
import {createStore} from 'redux'; import reducer from './reducer'; // 创建store对象,内部会第一次调用reducer()得到初始状态值 export default createStore(reducer);
-
-
reducer.js函数模块,根据当前state和指定action返回一个新的state-
import {INCREMENT,DECREMENT} from './action-types' // 管理count状态数据的reducer 注:一般情况下需要管理什么数据,函数名就叫什么名字 export default function count(state=2,action) { // 参数是固定的 (state指的就是数据本身) state=2 设置默认值 switch (action.type) { case INCREMENT: return state + action.data case DECREMENT: return state - action.data default: return state; } } (action需要通过 Action Creator(创建Action的工厂函数) ==> 下面的action.js) action的结构:标识要执行行为的对象 type:标识属性,值为字符串,唯一,必要性 xxx :数据属性,值类型任意,可选属性 //const action ={ type='INCREMENT',data:2 }
-
-
action-types.js用来存储常见常量,防止写错-
export const INCREMENT = 'increment'; export const DECREMENT = 'decrement';
-
-
action.js包含n个用来创建action的工厂函数(action creator)-
import {INCREMENT,DECREMENT} from './action-types' // 增加的action export const incre = (num) => ({type:INCREMENT,data:num}); // 减少的action export const decre = (num) => ({type:DECREMENT,data:num})
-
-
-
数据已经存储好了,下面看 store 怎么使用:(由于数据比较少,直接在App.js中写的操作)
-
App.js获取store中的数据-
render(){ const count = this.props.store.getState(); // 1、这里是从props中获取的,所以需要 通过 index.js将 store传递过来 // 2、调用store.getState() 方法获取 }
-
-
调用
action.js中的方法,操作(改变)store中的数据-
App.js 1、引入 action.js import {incre,decre} from './redux/actions'; 2、在 App.js相关的方法中,调用action中的方法 increment = ()=> { const number = this.numberRef.current.value * 1; this.props.store.dispatch(incre(number)); // 调用action中的方法,改变store中的数据 }
-
-
在
index.js将 store 绑定到 props 上
/**
* 入口JS
*/
import React from 'react';
import ReactDom from 'react-dom';
import App from './App';
import store from './redux/store';
ReactDom.render(<App store={store} />,document.getElementById('root'));
-
store中数据的改变,不会触发组件的更新
// 默认情况下 store中的数据发生改变,不会触发组件的更新 store.subscribe(() => { // store内部的状态数据发生改变时的回调 // 重新渲染APP组件标签 ReactDom.render(<App store={store} />,document.getElementById('root')); 所以在入口 js index.js 中 添加下面的代码
react-redux 介绍
一个react插件库,专门用来简化react应用中的redux
react-redux将所有组件分为两大类
UI组件
a:只负责UI的呈现,不带有任何业务逻辑
b:通过props接收数据(一般数据和函数)
c:不使用任何redux的API
d:一般保存在 components 文件夹下
容器组件
a:负责管理数据和业务逻辑,不负责ui的呈现
b:使用redux的API
c:一般保存在container文件夹下
react-redux 基本使用
该教程是基于👆redux,在redux的基础上进行下面的操作
react-redux语法功能分析
-
react-redux 向外暴露了两个API
a:Provider组件类
Provider 组件接收 store 属性,让所有组件都可以看到store,从而通过store读取/更新状态b:connect函数
接收两个参数:mapStateToProps 和 mapDispatchToProps
mapStateToProps:为一个函数,用来指定向UI组件传递哪些一般属性
mapDispatchToProps:为一个函数或对象,用来指定向UI组件传递哪些函数属性
connect 执行的返回值为一个高阶组件,包装UI组件,返回一个新的容器组件,容器组件会向UI组件传入前面指定的一般/函数类型属性
<Provider store={store}>
connect(
state=({xxx.state.xxx})
{actionCreator1,actionCreator2},
)(UI组件)
</Provider>
产生的就是容器组件,负责向UI组件传递标签属性,
一般属性值从state中获取,函数属性内部会执行dispatch分发action
- 安装
npm install --save react-redux - 删除
src -> App.js, 在src-> 新建container -> App.js, 在src新建components -> Counter.js Counter.js主要是 UI界面的呈现
特别注意:redux中的数据和方法获取,是直接从 props 中获取的
import React, { Component } from 'react';
import PropTypes from 'prop-types';
/**
* UI组件
*/
class Counter extends Component {
static propTypes = {
count: PropTypes.number.isRequired,
incre: PropTypes.func.isRequired,
decre: PropTypes.func.isRequired
}
constructor(props) {
super(props);
this.numberRef = React.createRef();
}
increment = ()=> {
const number = this.numberRef.current.value * 1;
this.props.incre(number);
}
decrement = ()=> {
const number = this.numberRef.current.value * 1;
// 调用redux中的方法,操作redux中的数据
this.props.decre(number);
}
render() {
// 获取 redux 中的 count
const count = this.props.count;
return (
<div>
<p>click {count} times</p>
<div>
<select ref={this.numberRef}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
<div className="btnGroup">
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
</div>
</div>
</div>
);
}
}
export default Counter;
container -> App.js
container 中的 组件被称为 容器组件,通过connect包装UI组件产生新的组件
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {incre,decre} from './../redux/actions'
import Counter from './../components/Counter';
/**
* 用来将redux管理的state数据映射成UI组件的一般属性的函数
*/
function mapStateToProps(state) {
return {
count: state
}
}
/**
* 将包含dispatch代码的函数映射成UI组件的函数属性的函数
*
*/
function mapDispatchToProps(dispatch) {
return {
incre: (number)=>dispatch(incre(number)),
decre: (number)=>dispatch(decre(number))
}
}
/**
* map 映射的意思
*/
export default connect(
mapStateToProps, // 指定一般属性
mapDispatchToProps, // 指定函数属性
)(Counter);
/**
* 高阶组件:
* 接收一个组件,返回一个组件函数
* connect() 高阶函数,返回的函数是一个高阶组件,接收一个UI组件,生成一个容器组件
* 容器组件的责任:向UI组件传入特定的属性
*/
上面代码简化版本:
import React, { Component } from 'react';
import {connect} from 'react-redux';
import {incre,decre} from './../redux/actions'
import Counter from './../components/Counter';
// 最终的简化版本(如果看不懂,请看👆)
export default connect(
state=> ({count:state}),
{incre,decre}
)(Counter);
- 修改
src->index.js
/**
* 入口JS
*/
import React from 'react';
import ReactDom from 'react-dom';
import App from './container/App';
import store from './redux/store';
import {Provider} from 'react-redux';
// ReactDom.render(<App store={store} />,document.getElementById('root'));
// // 默认情况下 store中的数据发生改变,不会触发组件的更新
// store.subscribe(() => { // store内部的状态数据发生改变时的回调
// // 重新渲染APP组件标签
// ReactDom.render(<App store={store} />,document.getElementById('root'));
// })
ReactDom.render((
<Provider store={store}>
<App />
</Provider>
),document.getElementById('root'));
// Provider 会监听数据的变化,并更新组件
redux-thunk 介绍
用来实现redux异步的中间件插件(默认redux是无法进行异步操作的)
npm install --save redux-thunk
-
在
store.js使用redux-thunkimport {createStore,applyMiddleware} from 'redux'; import reducer from './reducer'; // redux-thunk只有一个函数 thunk // 用来实现redux异步的中间件插件 import thunk from 'redux-thunk'; export default createStore(reducer,applyMiddleware(thunk)); -
在
action.js中新增一个异步操作import {INCREMENT,DECREMENT} from './action-types'; export const incre = (number)=>({type:INCREMENT,data:number}); // 返回对象 export const decre = (number)=>({type:DECREMENT,data:number}); // 增加异步的action,返回的是函数 export const increAsync = number => { return dispatch => { /** * 执行异步操作(定时器,ajax请求,promise) */ setTimeout(() => { // 当异步任务执行完成时,分发一个同步action dispatch(incre(number)); }, 1000); } } -
在
container->App.js引入 increAsync 异步操作的方法 -
在相对应的UI界面中调用
combineReducers 整合多个reducer
-
在
reducer.js-
导入
import {combineReducers} from 'redux'// 根据当前state和action返回一个指定的action import {INCREMENT,DECREMENT} from './action-types'; import {combineReducers} from 'redux' function count (state=1,action) { switch (action.type) { case INCREMENT: return state + action.data; case DECREMENT: return state - action.data; default: return state; } } function user (state ={},action) { switch (action.type) { default: return state; } } export default combineReducers({ count, user }) // export default count;
-
-
但是此时项目会报错,因为 我们 在
container -> App.js中代码写错了function mapStateToProps(state) { return { count: state.count // 原本是 count: state } }