三大核心概念
/*redux三大核心概念1. store:数据仓库2. action:通知对象3. reducer:数据处理函数/计算者*//*store: 数据仓库,一个项目中只有一个store,它是存放公共数据的地方如何创建store:1. 下载依赖: `yarn add redux`2. 创建store仓库*/import { legacy_createStore } from "redux";// import { createStore } from "redux";// 该函数在创建store时会被调用一次,用来初始化stateconst store = legacy_createStore(reducer);// reducer undefined {type: '@@redux/INITx.j.s.6.m.i'}console.log('store', store.getState()); // store 原数据/*action: 通知对象,用来描述一个改变数据的操作,是一个对象形式,必须含有type属性,用来描述改变的数据类型该字段一般要语义化,比如:修改修改文本框的值,type属性值应该是“change_text”至于action的其他属性,可以自定义,一般是后续计算数据所需要的参数每次改变数据的时候,必须向store发送action,来描述改变的数据*/const action = {type: "change_text",payload: "修改后的数据"}// 调用store.dispatch(action),会调用reducer函数,并且把action传入reducer函数store.dispatch(action);/*reducer: 数据处理函数/计算者,负责数据修改的逻辑,用来处理action,接收action,返回新的state它是一个方法,接收两个参数:1. state:当前的state,如果没有,则会使用默认的state2. action:表示接收到的action通知对象该函数的返回值,就是新的statereducer中的逻辑永远都是根据action中的type属性来判断返回不同的数据*/function reducer (state = { text: '原数据' }, action) {console.log('reducer', state, action);// reducer 原数据 {type: 'change_text', payload: 'hello redux'}switch (action.type) {case 'change_text':return {// 引用本身必须被修改,不然页面不会更新...state,text: action.payload};default:return state;}}console.log('修改后的store', store.getState()); // 修改后的store 修改后的数据
组件中使用store的数据
1. 准备好store,并暴露出去
import { createStore } from 'redux'//暴露storeexport default createStore(reducer);//reducerfunction reducer (state = {num: 0,}, action) {switch (action.type) {case 'changeNum':return {...state,//复制老的数据num: state.num + action.payload,//你要修改的数据};default: return state;//初始化数据}}
2. 把store中的数据注入到组件树的最上层
在App.js或者mian.js中都可以
//在App.jsimport store from './store/index';import { Provider } from 'react-redux'export default class App extends React.Component {render () {return (<Provider store={store}><HashRouter><Switch><Redirect from="/" to="login" exact /><Route path="/login" component={Login} /><Route path="/reg" component={Reg} /><Route path="/home" component={Home} /></Switch></HashRouter></Provider>)}}
// 引入状态机import { store } from './store'// Provider组件,用来把store传递给所有的子组件import { Provider } from 'react-redux'ReactDOM.createRoot(document.getElementById('root')).render(<Provider store={store}><App /></Provider>)
3.在组件中订阅store的数据
- 函数组件中
hooks中的useSelector、useDispatchconnect中的mapStateToProps、mapDispatchToProps
- 类组件中
// hook export default function Counter () { // 函数组件中使用useSelector可以获取store中的数据 // 使用useDispatch可以获取store中的dispatch方法 const dispatch = useDispatch() const count = useSelector(state => { // console.log(state); // 返回值就是当前组件需要的数据 return state.count }) // useEffect依赖项传入空数组,模拟componentDidMount useEffect(() => { const timer = setInterval(() => { dispatch({ type: ‘counter_increment’, payload: 1 }) }, 1000); // 清除函数 return () => { clearInterval(timer) } }, []) return (
// 使用connect中的mapStateToProps、mapDispatchToProps function Counter (props) { // props中的dispatch就是store中的dispatch方法 // console.log(‘props’, props); useEffect(() => { const timer = setInterval(() => { props.dispatch({ type: ‘counter_increment’, payload: 1 }) }, 1000); return () => { clearInterval(timer) } }, []) return (
<a name="oLjJo"></a>#### 类组件中```jsximport React, { Component } from 'react'import { connect } from 'react-redux'class Cart extends Component {// 加减数量changeNum = (id, type) => {this.props.dispatch({type: 'changeCartNum',payload: {id, type}})}// 删除商品deleteCartItem = (id) => {this.props.dispatch({type: 'deleteCartItem',payload: {id}})}// 计算总价get total () {let total = 0;this.props.list.forEach(item => {total += item.num * item.price;})return total;}render () {// props中的dispatch就是store中的dispatch方法console.log(this.props);return (<div><table><thead><tr><th>商品名</th><th>单价</th><th>数量</th><th>小计</th><th>操作</th></tr></thead><tbody>{this.props.list.map(item => {return (<tr key={item.name}><td>{item.name}</td><td>{item.price}</td><td><button onClick={() => this.changeNum(item.id, -1)}>-</button>{item.num}<button onClick={() => this.changeNum(item.id, 1)}>+</button></td><td>{item.num * item.price}</td><td><buttononClick={() => this.deleteCartItem(item.id)}>删除</button></td></tr>)})}</tbody></table>总价:{this.total}</div>)}}//该方法的作用是把当前组件所需要的数据,从store中取出来给到当前组件const mapStateToProps = (state) => {//形参state表示store中的所有数据// console.log('state', state)return {//key(给到组件的属性名):value(属性值)// reducer拆分前// list: state.cartListlist: state.cart.cartList}}// connect方法的作用是把store中的dispatch方法给到当前组件,传入的第一个参数会在内部执行函数export default connect(mapStateToProps)(Cart)
reducer拆分
当项目越来越大的时候,需要管理的数据也会越来越多,如果所有的数据都由一个reducer管理的话,则这个reducer肯定会变得非常的臃肿,且难以维护。所以有必要对reducer做一 个拆分,不同功能模块的数据切片,由不同的reducer来管理。
类似于vuex中modules
假设现在有两个模块,账户管理模块和商品管理模块,每个模块都有数据需要管理
//合并reducerimport { combineReducers } from 'redux';//管理账户模块数据切片的reducerfunction accountsReducer (state = {}, action) {switch (action.type) {default: return state;}}//管理商品模块数据切片的reducerfunction goodsReducer (state = {}, action) {switch (action.type) {default: return state;}}//combineReducers执行过后会返回一个大的reducer,const bigReducers = combineReducers({//key(当前数据切片的名字):value(管理该数据切片的reducer)accounts:accountsReducer,goods:goodsReducer,})//把bigReducer传入createStore并暴露export const store = createStore(bigReducers);
拆分reducers后,在组件中使用时,需要加上命名空间
