redux是专门为react设计的状态管理工具。虽然专门为react设计,但是也可以脱离react独自使用。
redux的状态管理思想是:将整个应用的所有状态都集中在一个唯一的store中进行管理,store中的状态只能通过设定的方法进行修改。
这样做的好处是可以保证store中的状态都是以可预测的形式进行变化,易维护,易监听。
在react项目中使用redux要下载redux和react-redux两个依赖项。
npm i redux --save
npm i react-redux --save
npm i redux-thunk -save:需要异步操作的话
代码展示:
./src/store/reducer.js
import {combineReducers} from "redux"
// 创建redux的store,需要先指定store中的状态有哪些
// 使用函数定义一个store中的状态。函数名就是状态名。
// 次函数有两个参数:1.state表示这个状态的初始值 2.action表示对这个状态的修改行为
function num(state=100,action){
console.log("num状态");
switch (action.type) {
case "NUM_DECREASE":
return state - 1;
case "NUM_INCREASE":
return state + 1;
case "SET_NUM":
return action.content;
default:
return state;
}
}
function txt(state="helloworld",action){
switch (action.type) {
case "SET_TXT":
return action.content;
case "CLEAR_TXT":
return "";
default:
return state;
}
}
function obj(state={name:"张",age:18},action){
switch (action.type) {
case "SET_OBJ_NAME":
// 对于非基本数据类型的改变,例如修改对象的属性或者修改数组中的元素,react的推荐做法是对
// 源数据直接进行拷贝然后赋值修改,这样可以保证历史状态不被修改。
// return Object.assign({},state,{name:action.content});
return {...state,...{name:action.content}}
default:
return state;
}
}
function list(state=[],action){
switch (action.type) {
case "SET_LIST":
return action.content;
default:
return state;
}
}
// 所有的状态都准备完毕之后,需要使用 combineReducers 函数将所有的状态合并为一个 reducer 对象(本质是一个action处理函数)
// combineReducers 函数参数是一个对象,对象中要包含所有的状态数据。
export default combineReducers({num,txt,obj,list})
./src/store/actions.js
// 修改store中的状态,必须使用提交action的形式修改数据。
// 在actions.js中需要为每一个action都创建一个函数,函数中必须返回action对象。
// action本质是一个对象,其中必须包含type属性,用于表示本次修改数据的操作类型。
// 除了type之外,还可以添加任意其他属性作为本次修改操作的参数。
function numDecrease() {
return {
type: "NUM_DECREASE"
}
}
function numIncrease() {
return {
type: "NUM_INCREASE"
}
}
function setNum(content) {
return {
type: "SET_NUM",
content
}
}
function setTxt(content) {
return {
type: "SET_TXT",
content
}
}
function clearTxt() {
return {
type: "CLEAR_TXT",
}
}
function setObjName(content) {
return {
type: "SET_OBJ_NAME",
content
}
}
function setList(content) {
return {
type: "SET_LIST",
content
}
}
// 异步修改reducer中的状态
function requestList() {
// 异步修改的action是一个函数,函数总需要返回异步任务的promise(要做的操作)
return dispatch => {
let p = new Promise(function (resove, reject) {
// 利用延时器模拟一个网络请求
setTimeout(() => {
let data = [1, 2, 3, 4, 5];
resove(data);
}, 3000);
});
// 异步任务完成之后,还是需要通过提交同步的action行为修改数据(提交setList方法,修改reducer中的list)
p.then(data => {
dispatch(setList(data));
});
return p;
}
}
export {
numDecrease,
numIncrease,
setNum,
setTxt,
clearTxt,
setObjName,
requestList
}
./src/store/store.js
// 创建仓库对象
import {createStore,applyMiddleware} from "redux"
// 导入所有的状态
import reducer from "./reducer"
// 如果要使用异步数据修改action,则需要引入异步操作的中间件 thunk
import thunk from "redux-thunk"
// 使用 createStore 函数创建 store 对象,参数:1.是状态reducer 2.导入的各种中间件
export default createStore(reducer,applyMiddleware(thunk));
./src/views/MyCom.js
import React from "react"
import "./MyCom.css"
import {connect} from "react-redux"
import {setTxt,clearTxt,setObjName,requestList} from "../store/actions"
class MyCom extends React.Component{
constructor(props){
super(props);
this.state = {};
}
render(){
return (
<div>
<hr/>
<p>{this.props.num}</p>
<p>{this.props.txt}</p>
<input type="text" value={this.props.txt} onChange={this.props.setTxt} />
<button onClick={this.props.clearTxt}>清除</button>
<p>{this.props.obj.name},{this.props.obj.age}</p>
<button onClick={()=>{this.props.setObjName(this.props.txt)}}>修改姓名</button>
<p>{this.props.list.toString()}</p>
<button onClick={this.props.requestList}>发起网络请求</button>
</div>
)
}
}
export default connect(state=>{
return {
num: state.num,
txt: state.txt,
obj: state.obj,
list: state.list
}
},dispatch=>{
return {
setTxt:(e)=>{
// console.log("我要修改txt的值:" + e.target.value);
dispatch(setTxt(e.target.value));
},
clearTxt:()=>{
dispatch(clearTxt());
},
setObjName: (name)=>{
dispatch(setObjName(name));
},
requestList:()=>{
dispatch(requestList()).then(function(){
console.log("数据请求完成");
});
}
}
})(MyCom);
./src/App.js
import React, { Component } from 'react';
import './App.css';
// 在组件中使用 store 中的状态,需要使用一个高阶组件 connect 函数
import {connect} from "react-redux"
// 在组件中导入要使用的action
import {numDecrease,numIncrease,setNum} from "./store/actions"
import MyCom from "./views/MyCom"
class App extends Component {
constructor(props){
super(props);
this.state = {}
}
render() {
return (
<div className="App">
<button onClick={this.props.numInc}>+</button>
{/* 从store中map到组件中的数据会存在组件的props属性中 */}
<span>{this.props.num}</span>
<button onClick={this.props.numDec}>-</button>
<br/>
<input type="number" onChange={(e)=>{this.props.setNum(e.target.value)}}/>
<MyCom></MyCom>
</div>
);
}
}
// connect有两个参数,
// 1.映射仓库中的状态,类型是一个回调函数,在回调函数中返回对应的状态
// 2.映射action中的函数,类型是一个回调函数,在回调函数中返回对应的action方法
// 返回值又是一个函数(高阶组件),功能是把组件改造为映射之后的新组件
/*let func = connect(state=>{
return {
num: state.num
}
},dispatch=>{
// dispath提交action中的方法
return{
numDec:()=>{
dispatch(numDecrease());
},
numInc:()=>{
dispatch(numIncrease());
}
}
});
export default func(App);*/
export default connect(state=>{
return {
num: state.num
}
},dispatch=>{
// dispath提交action中的方法
return{
numDec:()=>{
dispatch(numDecrease());
},
numInc:()=>{
dispatch(numIncrease());
},
setNum:(n)=>{
dispatch(setNum(n));
}
}
})(App);
// 流程:状态的声明->仓库的创建->仓库注入到项目中->组件使用仓具的数据->修改仓库的数据
// 异步操作状态数据,高级用法明天讲
// 作业:
// 1.通过看回放,练习redux基础使用
// 2.在reducer.js 文件中,除了num,之外再定义其他一些类型,然后在actions定义修改方法
// 比如:定义string,定义object(对象),定义列表(数组),添加修改方法(直接附上对应的值就行)
./src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
// 在react项目中使用redux,要在项目程序的入口文件中导入 Provider 组件
import {Provider} from "react-redux"
// 导入仓库对象
import store from "./store/store"
ReactDOM.render(
// 使用Provider组件将store注入到整个react项目中,注入之后在项目的所有组件都可以访问store
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);