1.简介
redux是一个专门用于状态管理的JS库,可用在React、Vue、Angular等项目中。
什么情况下要用到redux?
①某个组件的状态,需要让其它组件拿到(共享)。
②一个组件需要改变另一个组件的状态(通信)。
redux工作流程
Action:是一个普通JS对象,type字段用于描述发生事件的类型,payload字段用于描述发生的具体事情。
Action Creator:是一个创建并返回一个 action 对象的函数。
Reducer:是一个函数,接收当前的state和action对象,必要时决定如何更新状态,并返回新状态,可以将 reducer 视为一个事件监听器。
Store:当前 Redux 应用的 state 存在于一个名为 store 的对象中。store 是通过传入一个 reducer 来创建的,并且有一个名为getState的方法,它返回当前状态值。
dispatch:store有一个方法叫dispatch。更新 state 的唯一方法是调用store.dispatch()并传入一个action对象。store 将执行所有 reducer 函数并计算出更新后的 state,调用getState()可以获取新 state。dispatch一个action可以形象的理解为 "触发一个事件"。
React-Redux 是 React 的官方 Redux UI 绑定库。如果想要同时使用 Redux 和 React,你也应该使用 React-Redux 来绑定这两个库。
UI组件应该包裹一个容器组件,它们是父子关系。
UI组件不能使用任何redux的api,只负责页面的呈现和交互,容器组件负责和redux通信,容器组件可以传给UI组件:redux中保存的状态和用于操作状态的方法。
2.求和案例(纯React版本)
/public/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<title>Redux</title>
</head>
<body>
<div id="root"></div>
</body>
</html>
/src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
/src/App.jsx
import Count from "./components/Count";
function App() {
return (
<div>
<Count />
</div>
);
}
export default App;
/src/components/Count/index.jsx
import React, { Component } from 'react'
export default class Count extends Component {
state = {count:0}
increment=()=>{
const {value} = this.selectNumer
const {count} = this.state
this.setState({count:count+value*1})
}
decrement=()=>{
const {value} = this.selectNumer
const {count} = this.state
this.setState({count:count-value*1})
}
incrementAsync=()=>{
const {value} = this.selectNumer
const {count} = this.state
setTimeout(() => {
this.setState({count:count+value*1})
}, 500)
}
render() {
return (
<div>
<h1>当前求和为:{this.state.count}</h1>
<select ref={c => this.selectNumer = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
3.求和案例(redux版本)
npx create-react-app redux-demo //新建项目
npm install @reduxjs/toolkit
/src/components/Count/index.jsx
import React, { Component } from 'react'
import store from '../../redux/store'
import { createIncrementAction,createDecrementAction,createIncrementAsyncAction } from '../../redux/count_action'
export default class Count extends Component {
increment=()=>{
const {value} = this.selectNumer
store.dispatch(createIncrementAction(value*1))
}
decrement=()=>{
const {value} = this.selectNumer
store.dispatch(createDecrementAction(value*1))
}
incrementAsync=()=>{
const {value} = this.selectNumer
store.dispatch(createIncrementAsyncAction(value*1,500))
}
render() {
return (
<div>
<h1>当前求和为:{store.getState()}</h1>
<select ref={c => this.selectNumer = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
/src/redux/constant.js
/*
该模块用于定义action对象中type类型的常量值
*/
export const INCREMENT = 'increment'
export const DECREMENT = 'decrement'
/src/redux/count_action.js
/*
该文件专门为Count组件生成action对象
*/
import { INCREMENT, DECREMENT } from "./constant";
// 同步action,就是指action的值为一般object类型的对象
export const createIncrementAction = data => ({type:INCREMENT,payload: data})
export const createDecrementAction = data => ({type:DECREMENT,payload: data})
// 异步action,就是指action的值为函数
export const createIncrementAsyncAction = (data, time) => {
return (dispatch)=>{
setTimeout(()=>{
dispatch(createIncrementAction(data))
},time)
}
}
/src/redux/store.js
/*
该文件用于暴露一个store对象
*/
import {configureStore} from '@reduxjs/toolkit'
import countReducer from './count_reducer'
export default configureStore({ reducer: countReducer })
/src/redux/count_reducer.js
/*
该文件是用于创建一个为Count组件服务的reducer
*/
import { INCREMENT,DECREMENT } from "./constant";
const initState = 0;
export default function countReducer(preState=initState, action) {
const {type, payload} = action;
switch(type) {
case INCREMENT:
return preState+payload;
case DECREMENT:
return preState-payload;
default:
return preState;
}
}
/src/App.jsx同上
/src/index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './redux/store';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
// 添加一个变化监听器, 每当dispatch action的时候就会执行
store.subscribe(()=>{
root.render(<App />);
})
效果同上
4.求和案例(react-redux版本)
npm install react-redux
/src/components/Count/index.jsx文件删掉,换成/src/containers/Count/index.jsx
import {connect} from 'react-redux'
import React, { Component } from 'react'
// 引入action
import {
createIncrementAction,
createDecrementAction,
createIncrementAsyncAction
} from '../../redux/count_action'
// 定义CountUI组件
class CountUI extends Component {
increment=()=>{
const {value} = this.selectNumer
this.props.add(value*1)
}
decrement=()=>{
const {value} = this.selectNumer
this.props.subtract(value*1)
}
incrementAsync=()=>{
const {value} = this.selectNumer
this.props.addAsync(value*1,500)
}
render() {
return (
<div>
<h1>当前求和为:{this.props.count}</h1>
<select ref={c => this.selectNumer = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
// 创建并暴露一个Count的容器组件
export default connect(
state => ({count: state}),
{
add: createIncrementAction,
subtract: createDecrementAction,
addAsync: createIncrementAsyncAction
}
)(CountUI)
redux下面的四个文件不变,同上
/src/App.jsx同上
/src/index.js(index.js中不用监测了,容器组件会自动监测)
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
import store from './redux/store';
import { Provider } from 'react-redux'; // provider会自动帮容器组件传store
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App />
</Provider>
);
效果同上
小结:
一个组件要和redux“打交道”要经过哪几步?
①.定义好UI组件---不暴露
②.引入connect生成一个容器组件,并暴露,写法如下:
connect(
state => ({key:value}), //映射状态
{key:xxxxxAction} //映射操作状态的方法
)(UI组件)
③在UI组件中通过this.props.xxxxxxx读取和操作状态