- 英文文档: https://redux.js.org/
- 中文文档: http://www.redux.org.cn/
- Github: https://github.com/reactjs/redux
一、redux
1、redux是什么?
??1. redux是一个专门用于做状态管理 的JS库; ??2. 它可以用在react, angular, vue等项目中, 但基本与react配合使用; ??3. 作用: 集中式管理react应用中多个组件共享的状态。
2、什么时候需要使用redux?
- 某个组件的状态,需要让其他组件可以随时拿到(共享)。
- 一个组件需要改变另一个组件的状态(通信)。
- 总体原则:能不用就不用, 如果不用比较吃力才考虑使用。
3、redux工作流程
4、redux的三个核心概念
4.1、action
- 动作的对象
- 包含2个属性
? type:标识属性, 值为字符串, 唯一, 必要属性 ? data:数据属性, 值类型任意, 可选属性 - 例子:{ type: ‘ADD_STUDENT’,data:{name: ‘tom’,age:18} }
4.2、reducer
- 用于初始化状态、加工状态。
- 加工时,根据旧的state和action, 产生新的state的
纯函数 。
4.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时, 自动调用
5、redux的核心API
5.1、createstore()
??作用:创建包含指定reducer的store对象
5.2、store对象
- 作用: redux库最核心的管理对象
- 它内部维护着:
- state
- reducer
- 核心方法:
- getState(),获取状态
- dispatch(action),派发行为
- subscribe(listener),监测redux的状态变化
- 具体编码:
- store.getState()
- store.dispatch({type:‘INCREMENT’, number})
- store.subscribe(render)
5.3、applyMiddleware()
??作用:应用上基于redux的中间件(插件库)
5.4、combineReducers()
??作用:合并多个reducer函数
6、异步action
- redux默认是不能进行异步处理的,
- 某些时候应用中需要在redux中执行异步任务(ajax, 定时器)
- 使用异步中间件:npm install --save redux-thunk
7、redux的应用(求和案例)
运行结果
文件目录结构
1、创建store,store.js
import {createStore, applyMiddleware} from "redux";
import countReducer from "./count_reducer";
import thunk from "redux-thunk";
export default createStore(countReducer, applyMiddleware(thunk));
2、count_reducer
import {INCREMENT,DECREMENT} from "./constant";
const initState = 7;
function countReducer(preState = initState, action) {
console.log(preState, action);
const {type, data} = action;
switch (type) {
case INCREMENT://加
return preState + data;
case DECREMENT://减
return preState - data;
default:
return preState
}
}
export default countReducer
3、count_action.js
import {INCREMENT, DECREMENT} from "./constant";
import store from "./store";
export const createIncrementAction = data => ({type: INCREMENT, data})
export const createDecrementAction = data => ({type: DECREMENT, data})
export const createIncrementAsyncAction = (data, time) => {
return () => {
setTimeout(() => {
store.dispatch(createIncrementAction(data))
}, time)
}
}
4、constant.js
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
5、count/index.js
import React, {Component} from 'react';
import store from "../../redux/store";
import {createDecrementAction, createIncrementAction, createIncrementAsyncAction} from "../../redux/count_action";
export default class Count extends Component {
increment = () => {
const {value} = this.selectNumber;
store.dispatch(createIncrementAction(value * 1))
}
decrement = () => {
const {value} = this.selectNumber;
store.dispatch(createDecrementAction(value * 1))
}
incrementIfOdd = () => {
const {value} = this.selectNumber;
const count = store.getState()
if (count % 2 !== 0) {
store.dispatch(createIncrementAction(value * 1))
}
}
incrementAsync = () => {
const {value} = this.selectNumber;
store.dispatch(createIncrementAsyncAction(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>
<option value="4">4</option>
</select><br/><br/>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
监测redux的状态变化也可以在index.js入口文件中完成
import App from './App';
import store from "./redux/store";
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<App/>
</React.StrictMode>
);
store.subscribe(() => {
root.render(
<React.StrictMode>
<App/>
</React.StrictMode>
);
})
6、App.js 在App.js文件中引入组件count/index.js
二、react-redux
1、react-Redux将所有组件分成两大类
- UI组件
- 只负责 UI 的呈现,不带有任何业务逻辑
- 通过props接收数据(一般数据和函数)
- 不使用任何 Redux 的 API
- 一般保存在components文件夹下
- 容器组件
- 负责管理数据和业务逻辑,不负责UI的呈现
- 使用 Redux 的 API
- 一般保存在containers文件夹下
2、react-redux相关的API
2.1、Provider
??让所有组件都可以得到state数据
root.render(
<Provider store={store}>
<App/>
</Provider>
);
2.2、connect
??用于包装 UI 组件生成容器组件
export const CountContainer = connect(
mapStateToProps,
mapDispatchToProps)(CountUI)
2.3、 mapStateToprops
??将外部的数据(即state对象)转换为UI组件的标签属性
const mapStateToProps = (state) => {
return {count: state}
}
2.4、mapDispatchToProps
??将分发action的函数转换为UI组件的标签属性
3、react-redux的应用(求和案例)
-
运行效果 -
文件目录结构
3.components/count/index.js
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)
}
incrementIfOdd = () => {
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('UI组件', 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>
<option value="4">4</option>
</select><br/><br/>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
- containers/count/index.jsx
import {connect} from "react-redux";
import CountUI from "../../components/count";
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from "../../redux/count_action";
const mapStateToProps = (state) => {
return {count: state}
}
const mapDispatchToProps = (dispatch) => {
return {
jia: (data) => {
dispatch(createIncrementAction(data))
},
jian: (data) => {
dispatch(createDecrementAction(data))
},
jiaAsync: (data, time) => {
dispatch(createIncrementAsyncAction(data, time))
}
}
}
export const CountContainer = connect(
mapStateToProps,
mapDispatchToProps)(CountUI)
- store.js
import {createStore, applyMiddleware} from "redux";
import countReducer from "./count_reducer";
import thunk from "redux-thunk";
export default createStore(countReducer, applyMiddleware(thunk));
- count_reducer
import {INCREMENT,DECREMENT} from "./constant";
const initState = 7;
function countReducer(preState = initState, action) {
console.log(preState, action);
const {type, data} = action;
switch (type) {
case INCREMENT://加
return preState + data;
case DECREMENT://减
return preState - data;
default:
return preState
}
}
export default countReducer
- count_action.js
import {INCREMENT, DECREMENT} from "./constant";
import store from "./store";
export const createIncrementAction = data => ({type: INCREMENT, data})
export const createDecrementAction = data => ({type: DECREMENT, data})
export const createIncrementAsyncAction = (data, time) => {
return () => {
setTimeout(() => {
store.dispatch(createIncrementAction(data))
}, time)
}
}
- constant.js
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
- App.js
import {CountContainer} from "./containers/count";
import './App.css';
function App() {
return (
<div className="App">
<CountContainer/>
</div>
);
}
export default App;
- index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import store from "./redux/store";
import {Provider} from "react-redux"
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App/>
</Provider>
);
将UI组件和容器组件结合到一起
import {connect} from "react-redux";
import React, {Component} from 'react';
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from "../../redux/count_action";
class Count extends Component {
increment = () => {
const {value} = this.selectNumber;
this.props.jia(value * 1)
}
decrement = () => {
const {value} = this.selectNumber;
this.props.jian(value * 1)
}
incrementIfOdd = () => {
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('UI组件', 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>
<option value="4">4</option>
</select><br/><br/>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
export const CountContainer = connect(
state => ({count: state}),
{
jia: createIncrementAction,
jian: createDecrementAction,
jiaAsync: createIncrementAsyncAction
}
)(Count)
4、react-redux数据共享案例
效果:
文件目录:
containers/count/index.jsx
import {connect} from "react-redux";
import React, {Component} from 'react';
import {createIncrementAction, createDecrementAction, createIncrementAsyncAction} from "../../redux/actions/count";
class Count extends Component {
increment = () => {
const {value} = this.selectNumber;
this.props.increment(value * 1)
}
decrement = () => {
const {value} = this.selectNumber;
this.props.decrement(value * 1)
}
incrementIfOdd = () => {
const {value} = this.selectNumber;
if (this.props.count % 2 !== 0) {
this.props.increment(value * 1)
}
}
incrementAsync = () => {
const {value} = this.selectNumber;
this.props.incrementAsync(value * 1, 500)
}
render() {
console.log('UI组件', this.props);
return (
<div>
<h1>我是count组件</h1>
<h1>当前求和为:{this.props.count}</h1>
<h1>下方组件总人数为:{this.props.personTotal}</h1>
<select ref={c => this.selectNumber = c}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
</select><br/><br/>
<button onClick={this.increment}>+</button>
<button onClick={this.decrement}>-</button>
<button onClick={this.incrementIfOdd}>当前求和为奇数再加</button>
<button onClick={this.incrementAsync}>异步加</button>
</div>
)
}
}
export const CountContainer = connect(
state => ({
count: state.countReducer,
personTotal: state.personReducer.length
}),
{
increment: createIncrementAction,
decrement: createDecrementAction,
incrementAsync: createIncrementAsyncAction
}
)(Count)
containers/person/index.jsx
import React, {Component} from 'react';
import {nanoid} from "nanoid"
import {connect} from "react-redux";
import {createAddPersonAction} from "../../redux/actions/person"
class Person extends Component {
addPerson = () => {
const name = this.nameNode.value;
const age = this.ageNode.value;
const personObj = {id: nanoid(), name, age};
console.log(personObj);
this.props.addOnePerson(personObj);
this.nameNode.value = "";
this.ageNode.value = "";
}
render() {
return (
<div>
<h1>我是person组件</h1>
<h1>上方组件的和为:{this.props.sum}</h1>
<input ref={c => this.nameNode = c} type="text" placeholder="输入名字"/>
<input ref={c => this.ageNode = c} type="text" placeholder="输入年龄"/>
<button onClick={this.addPerson}>添加</button>
<ul>
{this.props.person.map((item) => {
return (<li key={item.id}>名字:{item.name}----年龄:{item.age}</li>)
})}
</ul>
</div>
)
}
}
export const PersonContainer = connect(
state => ({
person: state.personReducer,
sum: state.countReducer
}),
{addOnePerson: createAddPersonAction}
)(Person)
redux/actions/count.js
import {INCREMENT, DECREMENT} from "../constant";
import store from "../store";
export const createIncrementAction = data => ({type: INCREMENT, data})
export const createDecrementAction = data => ({type: DECREMENT, data})
export const createIncrementAsyncAction = (data, time) => {
return () => {
setTimeout(() => {
store.dispatch(createIncrementAction(data))
}, time)
}
}
redux/actions/person.js
import {ADD_PERSON} from "../constant";
export const createAddPersonAction=personObj=>({type:ADD_PERSON,data:personObj})
redux/reducers/count.js
import {INCREMENT,DECREMENT} from "../constant";
function countReducer(preState = 7, action) {
console.log(preState, action);
const {type, data} = action;
switch (type) {
case INCREMENT://加
return preState + data;
case DECREMENT://减
return preState - data;
default:
return preState
}
}
export default countReducer
redux/reducers/person.js
import {ADD_PERSON} from "../constant"
export default function personReducer(prevState = [{id: "001", name: "tom", age: 12}], action) {
const {type, data} = action;
switch (type) {
case ADD_PERSON:
return [data, ...prevState];
default:
return [...prevState]
}
}
redux/reducers/index.js:用于汇总多个reducer
import {combineReducers} from "redux";
import countReducer from "./count";
import personReducer from "./person";
const allReducer = combineReducers({
countReducer, personReducer
})
export default allReducer;
redux/costant.js
export const INCREMENT = "increment";
export const DECREMENT = "decrement";
export const ADD_PERSON="addPerson"
redux/store.js
import {createStore, applyMiddleware} from "redux";
import {composeWithDevTools} from "redux-devtools-extension";
import allReducer from "./reducers";
import thunk from "redux-thunk";
export default createStore(allReducer, composeWithDevTools(applyMiddleware(thunk)));
App.js
import {CountContainer} from "./containers/count";
import {PersonContainer} from "./containers/person";
import './App.css';
function App() {
return (
<div className="App">
<CountContainer/>
<hr/>
<PersonContainer/>
</div>
);
}
export default App;
index.js
import React from 'react';
import ReactDOM from 'react-dom/client';
import store from "./redux/store";
import {Provider} from "react-redux"
import './index.css';
import App from './App';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<Provider store={store}>
<App/>
</Provider>
);
|