redux 使用

831 阅读5分钟

写在前面的话:

redux 可结合react等库一起使用,下面先介绍单独使用redux时的用法。

用法一

让我们结合一个计数器案例来看它的使用吧 😊
假设页面上分别由加、减两个button 和 一个span标签(如下), 我们想通过点击button,实现count的加减。
  <button id = 'plus'>  + </button>
  <span id='count'> 0 </span>
  <button id = 'minus'> - </button>
一. 引入cdn版本
    <script src='https://cdn.bootcdn.net/ajax/libs/redux/4.1.0/redux.js'></script>

这样就在全局得到一个Redux对象

1. 创建store
 const store = Redux.createStore(reducer)
3. 定义初始状态
    const intitalState = {
          count: 0,
    }
2. 定义reducer
    const reducer = (state = intitalState, action)=> {
      switch (action.type){
        case "increment":
          return {
            count: state.count +1
          };
          case "decrement":
            return {
              count: state.count - 1
            };
          default: 
          return state
      }
    }
4. 定义action
    const increment = {
      type: "increment"
    }
    const decrement = {
      type: "decrement"
    }
5. 触发action
    const plus = document.getElementById("plus")
    const minus = document.getElementById("minus")
    plus.addEventListener('click', () => {
      store.dispatch(increment)
    })
    minus.addEventListener('click',() => {
      store.dispatch(decrement)
    })
6. 订阅store,状态发生改变时手动更新
store.subscribe(() => {
    const el = document.getElementById("count");
    el.innerHTML = store.getState().count
})

总结 redux的核心api

    //1. 创建Store容器
    const store = Redux.createStore(reducer)
    //2. 创建处理状态的reducer
    function reducer (state, action) {
    // 函数的返回值即为store对象
    // reducer 函数接收两个参数
      // state 即为向store中存储的状态,(可通过createStore的第二个参数指定,也可以通过函数默认参数的形式制定)
      // 即为触发的action对象,被reducer的第二个参数接收。 可根据action中type值的不同,对store中的状态进行处理
    }
    //3. 获取状态
    store.getState()
    //4. 订阅状态
    store.subscribe(function () {
    //当store中的状态发生变化时,store就会执行subscribe中传递的函数
    //通常使用这个方法得到store的最新状态以更新视图
    })
    //5. 触发action
    store.dispatch({type: 'xxxx'}) //通过调用dispatch 触发 action


用法二. redux 在react中的使用

首先看下图,了解redux 真正的工作流程:

image.png

接下来,我们结合react-redux 一步步来改造下上面的案例。

1.安装项目依赖
  • yarn add redux react-redux
2.代码替换

然后在index.js中定义好Counter组件,替换掉App组件。
并把我们之前写的代码搬过来 如下:

import { createStore } from 'redux'

const intitalState = {
  count: 0,
}

const reducer = (state = intitalState, actions) => {
  switch (actions.type) {
     case "increment":
          console.log("increment")
         return {
            count: state.count +1
          };
     case "decrement":
       console.log("increment")
        return {
          count: state.count - 1
        };
     default: 
      return state
  }
}

const store = createStore(reducer);
console.log(store.getState(), "state");

const increment = { type: 'increment' };
const decrement = { type: 'decrement' };

function Counter () {
  return (
      <div>
          <button id = 'plus' onClick = {() => store.dispatch(increment)}>
              +
           </button>
           <span id='container'>
              0
           </span>
           <button id = 'minus' onClick = {() => store.dispatch(decrement)}>
              -
            </button>
      </div>
 )}

store.subscribe(() => {
  console.log(store.getState())
})

ReactDOM.render(
  <React.StrictMode>
    {/* <App /> */}
    <Counter/>
  </React.StrictMode>,
  document.getElementById('root')
);

3.调试看页面展示效果
如代码所示,我们为button绑定了点击事件。
切到浏览器,尝试点击button,控制台中可以看到如下结果:

image.png

由此可见,我们确实能成功的更改store中的状态。
so,当状态发生改变时,我们应该同步给视图。
即,当状态发生改变时,我们需要重新去渲染下组件。

那么,我们这样来做:


function Counter () {
  return <div>
  <button id = 'plus' onClick = {() => store.dispatch(increment)}>
      +
    </button>
    <span id='container'>
      {/* 组件中获取store的最新状态 */}
      {store.getState().count} 
    </span>
    <button id = 'minus' onClick = {() => store.dispatch(decrement)}>
      -
    </button>
  </div>
}

store.subscribe(() => { // store中的状态发生改变时,我们再次去渲染Counter组件
  ReactDOM.render(
    <React.StrictMode>
      <Counter/>
    </React.StrictMode>,
    document.getElementById('root')
  );
})


ReactDOM.render( //初识渲染组件
  <React.StrictMode>
    {/* <App /> */}
    <Counter/>
  </React.StrictMode>,
  document.getElementById('root')
);
总结

我们可以看到视图确实更新了。
但却存在很多问题,只渲染个Counter组件,我们就写了两遍,并在其他组件里我们也没办法拿到store对象等。

下一节,我们就一起来看下react 和 redux是如何结合在一起的。

用法三: react-redux 与 redux的结合

前置知识:

react-redux里包含:

1. Provider组件:
 作用:把我们创建的store 放在全局,各个组件都能够得到的地方。 <br/>
 用法: Provider组件要包裹项目中所有的组件,即应该放在最外层组件上
2. connect方法:
 作用:
    1. 帮助我们订阅store,当store中的状态发生改变时,会帮我们重新渲染组件。
    2. 帮助我们拿到store中的状态,将store中的状态通过组件的props属性映射给组件。
    3. 通过connect方法,我们可以获取dispatch方法,通过dispatch方法来调用action。
     
 使用:<br/>
      调用connect方法会在返回一个方法,我们需要调用返回的方法,并把组件作为参数传递进去。<br/>
      (因为当store的状态发生变化时,需要知道要更新哪个组件,也需要知道store中的状态要映射给哪个组件)
      

🌰

    import { connect } form 'react-redux';
    export default connect()(Counter)

获取store中的状态并映射到组件的props中:
connect方法的调用有一个形参,是个函数,这个函数接收一个形参,即为store中的state 🌰

const mapStateToProps = state => {//返回一个对象,返回的对象会映射到组件的pros中
    return {
        count: state.count
    }
}
 export default connect(mapStateToProps)(Counter)
3. connect方法的第二个参数:

将dispatch映射到组件的props中,在组件中就可以通过调用props.xxx来触发action

🌰

 const mapDispatchToProps = dispatch => {//返回一个对象,返回的对象同样会映射到组件的pros中
    return {
       increment(){
          dispatch({type: 'increment'})
      },
      decrement(){
          dispatch({type: 'decrement'})
      }
    }
}
 export default connect(mapStateToProps, mapActionToProps)(Counter)
4. bindActionsCreators

仔细观察上面代码中的increment,decrement方法都调用了duspatch方法,结构也一致。所以我们想要简化代码,redux提供给了一个 bindActionsCreators方法来做这件事情,帮助我们生成我们想要的函数
🌰


// bindActionCreators的返回值即为你传入的方法。
  const mapDispatchToProps = (dispatch) => bindActionCreators({
    increment(){
        return { type: 'increment' }
    },
    decrement(){
        return { type: 'decrement' }
    }
  }, dispatch)

5. action 传参

我们在组件调用action地方携带参数,
然后store模块中定义action的函数里会默认接受到我们传递的参数 然后在reducer中就可以通过action.payload拿到我们传递的参数,来做相应的业务逻辑 🌰

 <button id='plus' onClick = {()=> increment(5)}> + </button> 
 // actions
 const increment = (payload) => ({ type: INCREMENT, payload })
 //reducer
 const reducer = (state, action) => {
     switch(action.type){
         case 'increment'
         return {
             count: state.count + action.payload
         }
         ......
     }
 }

以上是基本使用,我们再一起回顾并完善之前的代码吧

最后:
代码重构 & store各模块的抽离
 // src/store/index.js
import { createStore } from 'redux';
import reducer from './reducers/counter.reducer';
const store = createStore(reducer)
 // src/store/reducers/counter.reducer.js
import { INCREMENT, DECREMENT } from "../const/count.count";

const intitalState = {
    count: 0,
}
  
export default (state = intitalState, action) => {
    switch (actions.type) {
      case INCREMENT:
        return {
          count: state.count + action.payload
        };
        case DECREMENT:
          return {
            count: state.count - action.payload
          };
        default: 
        return state
    }
  }
  
    // src/index.js
    import React from 'react';
    import ReactDOM from 'react-dom';
    import App from './App';
    import { Provider } from 'react-redux';
    import { store } from './store';

    ReactDOM.render(
      <React.StrictMode>
        <Provider store={store}>
          <App />
        </Provider>
        </React.StrictMode>,
      document.getElementById('root')
    );
    
    // src/App
    import  Counter  from "./components/Counter";

    function App() {
      return (
        <div className="App">
         <Counter/>
        </div>
      );
    }

    export default App;
//src/store/actions/counter.actions.js
import { INCREMENT, DECREMENT } from "../const/count.count"

export const increment = (payload) => ({ type: INCREMENT, payload })
export const decrement = (payload) => ({ type: DECREMENT, payload })

// src/components/Counter
import { connect } from 'react-redux'
import { bindActionCreators } from 'redux'
import * as counterActions from '../store/actions/counter.actions'
function Counter ({count,increment, decrement}) {
    return (
        <div>
            <button id = 'plus' onClick = {()=> increment(5)}>
                +
            </button>
            <span id='container'>
                {count}
            </span>
            <button id = 'minus' onClick = {()=> decrement(5)}>
                -
            </button>
        </div>)
  }

  const mapStateToProps = (state) => ({
    count: state.count
  });

  const mapDispatchToProps = (dispatch) => bindActionCreators(counterActions, dispatch)

  export default connect(mapStateToProps, mapDispatchToProps)(Counter)
其他Api

当然redux中还有其他帮助我们减少代码模版的方法,比如combineReducer(用来合并reducer)等。