三大核心概念

  1. /*
  2. redux三大核心概念
  3. 1. store:数据仓库
  4. 2. action:通知对象
  5. 3. reducer:数据处理函数/计算者
  6. */
  7. /*
  8. store: 数据仓库,一个项目中只有一个store,它是存放公共数据的地方
  9. 如何创建store:
  10. 1. 下载依赖: `yarn add redux`
  11. 2. 创建store仓库
  12. */
  13. import { legacy_createStore } from "redux";
  14. // import { createStore } from "redux";
  15. // 该函数在创建store时会被调用一次,用来初始化state
  16. const store = legacy_createStore(reducer);
  17. // reducer undefined {type: '@@redux/INITx.j.s.6.m.i'}
  18. console.log('store', store.getState()); // store 原数据
  19. /*
  20. action: 通知对象,用来描述一个改变数据的操作,是一个对象形式,必须含有type属性,用来描述改变的数据类型
  21. 该字段一般要语义化,比如:修改修改文本框的值,type属性值应该是“change_text”
  22. 至于action的其他属性,可以自定义,一般是后续计算数据所需要的参数
  23. 每次改变数据的时候,必须向store发送action,来描述改变的数据
  24. */
  25. const action = {
  26. type: "change_text",
  27. payload: "修改后的数据"
  28. }
  29. // 调用store.dispatch(action),会调用reducer函数,并且把action传入reducer函数
  30. store.dispatch(action);
  31. /*
  32. reducer: 数据处理函数/计算者,负责数据修改的逻辑,用来处理action,接收action,返回新的state
  33. 它是一个方法,接收两个参数:
  34. 1. state:当前的state,如果没有,则会使用默认的state
  35. 2. action:表示接收到的action通知对象
  36. 该函数的返回值,就是新的state
  37. reducer中的逻辑永远都是根据action中的type属性来判断返回不同的数据
  38. */
  39. function reducer (state = { text: '原数据' }, action) {
  40. console.log('reducer', state, action);
  41. // reducer 原数据 {type: 'change_text', payload: 'hello redux'}
  42. switch (action.type) {
  43. case 'change_text':
  44. return {
  45. // 引用本身必须被修改,不然页面不会更新
  46. ...state,
  47. text: action.payload
  48. };
  49. default:
  50. return state;
  51. }
  52. }
  53. console.log('修改后的store', store.getState()); // 修改后的store 修改后的数据

组件中使用store的数据

1. 准备好store,并暴露出去

  1. import { createStore } from 'redux'
  2. //暴露store
  3. export default createStore(reducer);
  4. //reducer
  5. function reducer (state = {
  6. num: 0,
  7. }, action) {
  8. switch (action.type) {
  9. case 'changeNum':
  10. return {
  11. ...state,//复制老的数据
  12. num: state.num + action.payload,//你要修改的数据
  13. };
  14. default: return state;//初始化数据
  15. }
  16. }

2. 把store中的数据注入到组件树的最上层

App.js或者mian.js中都可以

  1. //在App.js
  2. import store from './store/index';
  3. import { Provider } from 'react-redux'
  4. export default class App extends React.Component {
  5. render () {
  6. return (
  7. <Provider store={store}>
  8. <HashRouter>
  9. <Switch>
  10. <Redirect from="/" to="login" exact />
  11. <Route path="/login" component={Login} />
  12. <Route path="/reg" component={Reg} />
  13. <Route path="/home" component={Home} />
  14. </Switch>
  15. </HashRouter>
  16. </Provider>
  17. )
  18. }
  19. }
  1. // 引入状态机
  2. import { store } from './store'
  3. // Provider组件,用来把store传递给所有的子组件
  4. import { Provider } from 'react-redux'
  5. ReactDOM.createRoot(document.getElementById('root')).render(
  6. <Provider store={store}>
  7. <App />
  8. </Provider>
  9. )

3.在组件中订阅store的数据

  • 函数组件中
    • hooks中的useSelectoruseDispatch
    • connect中的mapStateToPropsmapDispatchToProps
  • 类组件中
    • 只能使用connect

      函数组件中

      ```jsx import React, { useEffect } from ‘react’ import { useSelector, useDispatch, connect } from ‘react-redux’

// 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 (

Counter:{count}
) }

// 使用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 (

Counter:{props.count}
) } //该方法的作用是把当前组件所需要的数据,从store中取出来给到当前组件 const mapStateToProps = (state) => { //形参state表示store中的所有数据 // console.log(‘state’, state) return { //key(给到Counter组件的属性名):value(属性值) // reducer拆分前 // count: state.count count: state.counter.count } } // connect方法的作用是把store中的dispatch方法给到当前组件,传入的第一个参数会在内部执行函数 export default connect(mapStateToProps)(Counter)

  1. <a name="oLjJo"></a>
  2. #### 类组件中
  3. ```jsx
  4. import React, { Component } from 'react'
  5. import { connect } from 'react-redux'
  6. class Cart extends Component {
  7. // 加减数量
  8. changeNum = (id, type) => {
  9. this.props.dispatch({
  10. type: 'changeCartNum',
  11. payload: {
  12. id, type
  13. }
  14. })
  15. }
  16. // 删除商品
  17. deleteCartItem = (id) => {
  18. this.props.dispatch({
  19. type: 'deleteCartItem',
  20. payload: {
  21. id
  22. }
  23. })
  24. }
  25. // 计算总价
  26. get total () {
  27. let total = 0;
  28. this.props.list.forEach(item => {
  29. total += item.num * item.price;
  30. })
  31. return total;
  32. }
  33. render () {
  34. // props中的dispatch就是store中的dispatch方法
  35. console.log(this.props);
  36. return (
  37. <div>
  38. <table>
  39. <thead>
  40. <tr>
  41. <th>商品名</th>
  42. <th>单价</th>
  43. <th>数量</th>
  44. <th>小计</th>
  45. <th>操作</th>
  46. </tr>
  47. </thead>
  48. <tbody>
  49. {
  50. this.props.list.map(item => {
  51. return (
  52. <tr key={item.name}>
  53. <td>{item.name}</td>
  54. <td>{item.price}</td>
  55. <td>
  56. <button onClick={() => this.changeNum(item.id, -1)}>-</button>
  57. {item.num}
  58. <button onClick={() => this.changeNum(item.id, 1)}>+</button>
  59. </td>
  60. <td>{item.num * item.price}</td>
  61. <td>
  62. <button
  63. onClick={() => this.deleteCartItem(item.id)}>删除</button>
  64. </td>
  65. </tr>
  66. )
  67. })
  68. }
  69. </tbody>
  70. </table>
  71. 总价:{this.total}
  72. </div>
  73. )
  74. }
  75. }
  76. //该方法的作用是把当前组件所需要的数据,从store中取出来给到当前组件
  77. const mapStateToProps = (state) => {
  78. //形参state表示store中的所有数据
  79. // console.log('state', state)
  80. return {
  81. //key(给到组件的属性名):value(属性值)
  82. // reducer拆分前
  83. // list: state.cartList
  84. list: state.cart.cartList
  85. }
  86. }
  87. // connect方法的作用是把store中的dispatch方法给到当前组件,传入的第一个参数会在内部执行函数
  88. export default connect(mapStateToProps)(Cart)

reducer拆分

当项目越来越大的时候,需要管理的数据也会越来越多,如果所有的数据都由一个reducer管理的话,则这个reducer肯定会变得非常的臃肿,且难以维护。所以有必要对reducer做一 个拆分,不同功能模块的数据切片,由不同的reducer来管理。
类似于vuexmodules
假设现在有两个模块,账户管理模块和商品管理模块,每个模块都有数据需要管理

  1. //合并reducer
  2. import { combineReducers } from 'redux';
  3. //管理账户模块数据切片的reducer
  4. function accountsReducer (state = {}, action) {
  5. switch (action.type) {
  6. default: return state;
  7. }
  8. }
  9. //管理商品模块数据切片的reducer
  10. function goodsReducer (state = {}, action) {
  11. switch (action.type) {
  12. default: return state;
  13. }
  14. }
  15. //combineReducers执行过后会返回一个大的reducer,
  16. const bigReducers = combineReducers({
  17. //key(当前数据切片的名字):value(管理该数据切片的reducer)
  18. accounts:accountsReducer,
  19. goods:goodsReducer,
  20. })
  21. //把bigReducer传入createStore并暴露
  22. export const store = createStore(bigReducers);

拆分reducers后,在组件中使用时,需要加上命名空间