// 使用 npm 安装
npm i redux
创建 redux/store.js
文件,存放数据
// 用于创建一个 store
import { createStore } from "redux"
// 用于 Count 组件服务的 reducer
import countReducer from "./count_reducer.js"
export default createStore(countReducer)
创建 redux/count_reducer.js
文件,处理数据
import { ADD, SUB } from "./constant.js"
// preState:表示初始值,或前一次状态的值
export default function countReducer(preState = 0, action){
// action:它是一个对象,包含了方法和数据
let { type, data } = action
switch(type) {
case ADD:
return preState + data
case SUB:
return preState - data
default:
return preState
// 必须要返回一个新值或初始值
}
}
创建 redux/count_action.js
文件,提供方法
import { ADD, SUB } from "./constant.js"
// action 需要返回一个对象,type:方法名、data:数据
export const addAction = data => ({ type: ADD, data })
export const subAction = data => ({ type: SUB, data })
创建 redux/constant.js
文件,命名空间
// 因为 type 方法名称在多处都会使用到,这样写便于管理,还可以防止程序员写错
export const ADD = "add"
export const SUB = "sub"
组件中使用 store
import { Component } from "react";
import store from "./src/redux/store.js"
import { addAction, subAction } from "./src/redux/count_action.js"
export default class Count extends Component {
componentDidMount() {
/*
store 中的值发生了改变,并不会调用自动 render
当 store 中的值发生了改变,store.subscribe 会触发回调,然后使用 this.setState 强制调用 render
这样写有一点不好,就是每在一个组件使用一次,都需要再去写一遍,可以直接在入口文件中使用
这样只要有一个组件中的 redux 值发生了改变,就会更新视图
store.subscribe(() => {
ReactDOM.render( , document.getElement("root"))
})
*/
store.subscribe(() => {
this.setState({})
})
}
add = (data) => {
// dispatch 会触发 reducer 函数(这里指 countReducer 函数)
store.dispatch(addAction(data))
}
sub = (data) => {
store.dispatch(subAction(data))
}
render() {
return (
// getState 使用数据,store 本身是一个对象
<div>{ store.getState() }</div>
)
}
}
// 当 action 为函数时,它将会成为一个异步 action
import { createStore, applyMiddleware } from "redux"
// 引入 redux-thunk,用于支持异步 action
import thunk from "redux-thunk"
// 此时暴露时,需要执行中间件
export default createStore(countReducer, applyMiddleware(thunk))
// 这个时候写 action 时,就可以返回函数了
// 异步函数里面还是调用的同步函数
export const addAsyncAction = (data, time) => {
return (dispath) => {
setTimeout(() => {
dispath(addAction(data))
},time)
}
}
store.dispatch(addAsyncAction(data, 300))
npm i react-redux
容器组件,连接容器组件和UI组件
// count 容器组件,容器组件要写在 container 文件下,UI组件要写在 component 文件下
import CountUI from "./component/Count"
// 用于连接 UI 和 store,但此时只连接了 UI
import { connect } from "react-redux"
export default connect()(CountUI) // 有了 connect 函数不需要在手动检测数据的变化了
引入容器组件,连接容器组件和 store
// 使用组件时,要使用容器组件
import Count from "./container/Count"
// 使用时需要把 store 作为 prop 值传过去
import store from "./redux/store"
export default class Demo extends Componet {
render(){
return <Count store={store}></Count>
}
}
UI组件
// 正常写页面就可以了
// 容器组件
import { connect } from "react-redux"
import CountUI from "../component/Count"
import { addAction } from "../redux/count_action"
// 该函数返回一个存状态的对象,会把该对象的值映射到 CountUI 组件的 props 中
function mapStateToProps(state){
return {
// state ==> store.getState()
count: state
}
}
// 该函数返回一个存方法的对象,映射到 props 中
function mapDispatchToProps(dispatch){
return {
// dispatch ==> store.dispath
add: (data) => {
dispatch(addAction(data))
}
}
}
// 此时连接了 UI 和 store
export default connect(mapStateToProps, mapStateToProps)(CountUI)
// UI组件
export default class CountUI extends Componet {
return(){
console.log(this.porps) // {store: {…}, count: 0, add: ƒ},这里也可以拿到 store 对象
}
}
// 优化前
import { connect } from "react-redux";
import countUI from "../components/CountUI.jsx";
import { addAction, subAction, addAsyncAction } from "../redux/count_action";
export default connect(
(state) => ({
count: state,
}),
// 当 mapDispatchToProps 为对象时,可以直接把函数赋值过去
{
add: addAction,
sub: subAction,
addAsync: addAsyncAction,
}
)(countUI);
import React from "react";
import reactDOM from "react-dom";
import { Provider } from "react-redux";
import store from "./redux/store";
import App from "./App.jsx";
// 在这里使用 Provider 组件传入 store 值,使后续的所有组件都不需要再次传入 store
reactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("root")
);
import React, { Component } from "react";
import { connect } from "react-redux";
import { addAction, subAction, addAsyncAction } from "../redux/count_action";
// UI组件部分
class countUI extends Component {
add = () => {
let { value } = this.selectNode;
this.props.add(Number(value));
};
sub = () => {
let { value } = this.selectNode;
this.props.sub(Number(value));
};
addAsync = () => {
let { value } = this.selectNode;
this.props.addAsync(Number(value), 500);
};
render() {
let { count } = this.props;
return (
<div>
<div>
<h1>{count}</h1>
<select ref={(curNode) => (this.selectNode = curNode)}>
<option>1</option>
<option>2</option>
<option>3</option>
</select>
<button onClick={this.add}>点我加</button>
<button onClick={this.sub}>点我减</button>
<button onClick={this.addAsync}>异步加</button>
</div>
</div>
);
}
}
// connect 连接部分
export default connect (
(state) => ({
count: state,
}),
{
add: addAction,
sub: subAction,
addAsync: addAsyncAction,
}
)(countUI);
|-- redux // 数据处理的总文件夹
|-- reducers // 集中管理reducer
|-- demo1.js // 第一个reducer
|-- demo2.js // 第二...
|-- index.js // reducers的汇总
|-- actions // 集中管理action
|-- demo1.js // 第一个action
|-- demo2.js // 第二...
|-- constant.js // 命名空间
|-- sotre.js // 主文件,存放状态