美文网首页
react中redux的使用

react中redux的使用

作者: 踩坑怪At芬达 | 来源:发表于2021-09-26 14:36 被阅读0次

    以一个counter计数器组件为例,实现加减和异步加法

    实现效果图如下:

    image

    index.jsx -- 入口

    
    import React from 'react';
    import {createStore,combineReducers,applyMiddleware} from 'redux';
    import { Provider } from 'react-redux'
    import thunkMiddleware from 'redux-thunk'
    
    import * as reducers from './reducer/reducer.jsx';
    import Counter from './components/Counter.jsx';
    import LoginForm from './components/Login.jsx';
    
    /**
    * 创建store
    * reducers是什么?你可以查看下面reducer.jsx代码内容
    * combineReducers(reducers):是将多个reducer合并一起创建进store
    * applyMiddleware(thunkMiddleware):注入一个可以实现异步dispatch的插件,
    */
    let store = createStore(combineReducers(reducers),applyMiddleware(thunkMiddleware));
    
    // <Provider>:将store透传进所有子组件内,所有子组件都可以通过 react-dedux的connect获取store
    export default () => {
      return (
        <Provider store={store}>
            <Counter/>
        </Provider>
      );
    };
    
    

    Counter.jsx -- 计数器组件代码

    import React from 'react';
    import { connect } from 'react-redux'
    import {AddDelay,Add,Dec} from '../reducer/action.jsx';
    
    
    class Counter extends React.Component{
      constructor(props){
        super(props);
      }
    
      render(){
        const {reCounter,dispatch}=this.props;
        return (
          <div className={'flex-row flex-cross-center form'}>
            <p>当前计数器:{reCounter.value}</p>
            {/* 点击后计数器+1 */}
            <button onClick={()=>{dispatch( Add(1) )}}>加1</button>
            <button onClick={()=>{dispatch( Dec(1) )}}>减1</button>
             {/* 点击后计数器会在2秒后 +2 用来模拟接口调用 */}
            <button onClick={()=>{ dispatch( AddDelay(2,2000) )}}>
              {reCounter.loading?"loading":"加2 延迟2秒触发"}
            </button>
          </div>
        );
      }
    }
    
    /**
     * mapStateToProps的作用是将store传递进来的state,进行筛选后暴露给组件
     * 这里仅仅把store里面reCounter这个对象暴露给Countre组件
     * 所以Counter内可以通过 this.props.reCounter 获取到这个对象
     * state:store传递进来的所有reducer
     * return {} 你可以把你需要暴露给组件的对象筛选return,当然也可以直接将整个state return
     */
    function mapStateToProps(state) {
      return  {reCounter:state.reCounter};
    }
    
    /**
     * connect作用是将store和当前组件进行捆绑
     * mapStateToProps 函数作用是处理store传入的state对象然后选择需要暴露给组件用的state对象
     */
    export default connect(mapStateToProps)(Counter)
    
    
    

    redcuer.jsx -- 存放所有redcuer

    
    /**
     * 处理加减法
     */
    export function reCounter(state = {value:0,loading:false}, action) {
      let value = typeof action.value === 'undefined' ? 1 : action.value;
      switch (action.type) {
        case 'COUNTER_LOADING': return {...state, loading:action.value };
        case 'COUNTER_ADD':     return {...state, value:state.value + value };
        case 'COUNTER_DEC':     return {...state, value:state.value - value };
        default: return state;
      }
    }
    
    

    action.jsx -- 存放所有被组件直接使用的action,为什么需要action这一层,我这里做了个演示,一来是为了避免使用 dispatch({type:"消息名称",value:""}) 这种难看的写法

    二来是为了能统一处理一条消息的封装和发送

    
    /**
     * value:增加的值
     * time:延迟多少ms触发
     */
    export function AddDelay(value,time) {
      return (dispatch, getState) => {
        return new Promise(reslove => {
          dispatch({type:'COUNTER_LOADING',value:true});
          setTimeout(() => {
            dispatch({ type: 'COUNTER_ADD', value: value });
            dispatch({type:'COUNTER_LOADING',value:false});
            reslove();
          }, time);
        })
      }
    }
    
    /**
     * value:增加的值
     */
    export function Add(value) {
      return (dispatch, getState) => {
        return new Promise(reslove => {
          dispatch({ type: 'COUNTER_ADD', value: value });
          reslove();
        })
      }
    }
    /**
     * value:减少的值
     */
    export function Dec(value) {
      return (dispatch, getState) => {
        return new Promise(reslove => {
          dispatch({ type: 'COUNTER_DEC', value: value });
          reslove();
        })
      }
    }
    
    

    常见问题

    阻塞式dispatch更新值方法

    假设我们reducer管理一个叫user的对象{name:"姓名",detail:"我是姓名"}
    在组件内有一个方法用来监听name是否发生改变,一旦发生改变就重新设置detail的值
    当用户输入name发生变化时需要将detail设置为 我是${name}

    一开始你可能想这样处理:

    const User=(props)=>{
      const onNameChange= async (e)=>{
        //更新user下name属性
        await dispatch({...props.user,name:e.target.value});
       // 此处的监听可能存在于子组件中,这里为了方便展示所以直接下载了dispatch后
        await listener_nameChange(e.target.value);
        console.log(props.user);
      }
    
      //监听到name发生变更后,立刻更新了detail的值
     const listener_nameChange = async (newValue)=>{
        await dispatch({...props.user,detail:`i am ${newValue}`})
     }
    
      return (
        <div>
          <Input onChange={onNameChange} value={props.user.name} />
        </div>
      )
    }
    

    这里的log打印结果显然最终是detail发生了变更而name没有

    解决方法

    const User=(props)=>{
      onNameChange(e){
        //更新user下name属性
        dispatch({...props.user,name:e.target.value});
      }
       
      listener_nameChange(newValue){
        dispatch({...props.user,detail:`i am ${newValue}`})
      }
       // ✅ 正确方式应该在props值发生变更时监听,并且处理
      useEffect(()=>{
        listener_nameChange(props.user.name)
      },[props.user.name)
      
    
      return (
        <div>
          <Input onChange={onNameChange} value={props.user.name} />
          <Input value={props.user.detail} />
        </div>
      )
    }
    

    相关文章

      网友评论

          本文标题:react中redux的使用

          本文链接:https://www.haomeiwen.com/subject/ljgjzctx.html