Javascript 使用通用操作简化redux&;减速器

Javascript 使用通用操作简化redux&;减速器,javascript,reactjs,redux,react-redux,jsx,Javascript,Reactjs,Redux,React Redux,Jsx,在React-Redux项目中,人们通常为每个连接的组件创建多个动作和还原器。然而,这为简单的数据更新创建了大量代码 使用单个通用操作和缩减器来封装所有数据更改是否是一种好做法,以便简化和加快应用程序开发 使用这种方法会有什么缺点或性能损失。因为我看不出有什么重大的折衷,它使开发变得更加容易,我们可以将所有这些都放在一个文件中此类架构的示例: //假设我们在user.js中,用户页面 //陈述 var initialState={}; //通用操作-->我们只需要编写一个调度程序 功能设置状态(

在React-Redux项目中,人们通常为每个连接的组件创建多个动作和还原器。然而,这为简单的数据更新创建了大量代码

使用单个通用操作和缩减器来封装所有数据更改是否是一种好做法,以便简化和加快应用程序开发

使用这种方法会有什么缺点或性能损失。因为我看不出有什么重大的折衷,它使开发变得更加容易,我们可以将所有这些都放在一个文件中
此类架构的示例:

//假设我们在user.js中,用户页面
//陈述
var initialState={};
//通用操作-->我们只需要编写一个调度程序
功能设置状态(obj){
dispatch({type:'SET_USER',data:obj});
}
//通用减速机-->我们只需要编写一个动作减速机
函数userReducer=function(state=initialState,action){
开关(动作类型){
案例“SET_USER”:返回{…state,…action.data};
默认:返回状态;
}
};
//定义组件
var User=React.createClass({
render:function(){
//这就是魔法。。。
//我们可以调用泛型setState()来更新任何数据。
//无需创建单独的调度器和减速器,
//从而大大简化和加快了应用程序的开发。
返回[
setState({someField:1})}/>,
setState({someOtherField:2,randomField:3})}/>,
setState({orJustAnything:[1,2,3]})}/>
]
}
});
//为数据更新注册组件
函数MapStateTops(状态){
返回{…state.user};
}
导出默认连接(MapStateTops)(用户);

编辑 因此,典型的Redux架构建议创建:

  • 包含所有操作的集中化文件
  • 集中化文件和所有还原程序
  • 问题是,为什么要采用两步流程?下面是另一个架构建议:

    创建一组文件,其中包含处理所有数据更改的所有
    setXField()
    。其他组件只是使用它们来触发更改。容易的。例如:

    /**UserAPI.js
    *包含用户的所有方法。
    *其他组件可以直接调用它们。
    */
    //陈述
    var initialState={};
    //一般行为
    功能设置状态(obj){
    dispatch({type:'SET_USER',data:obj});
    }
    //通用减速机
    函数userReducer=function(state=initialState,action){
    开关(动作类型){
    案例“SET_USER”:返回{…state,…action.data};
    默认:返回状态;
    }
    };
    //我们出口的API
    让UserAPI={};
    //设置用户名
    UserAPI.setName=函数(名称){
    $.post('/user/name',{name},函数({ajaxSuccess}){
    if(ajaxSuccess)setState({name});
    });
    };
    //设置用户图片URL
    UserAPI.setPicture=函数(url){
    $.post('/user/picture',{url},函数({ajaxSuccess}){
    if(ajaxSuccess)setState({url});
    });
    };
    //注销,清除用户
    UserAPI.logout=函数(){
    $.post('/logout',{},function(){
    设置状态(初始状态);
    });
    };
    //等等,你想到了。。。
    //此外,您还可以添加一系列其他与用户相关的方法,
    //像一些与Redux无关的helper方法,或者Ajax getter。
    //现在,您可以在单个文件中获得与用户相关的所有内容!
    //它变得更容易通读和理解。
    //最后,您可以导出单个UserAPI对象,这样其他
    //组件只需要导入一次。
    导出默认用户API
    
    请通读上面代码部分的注释


    现在,不再有一堆操作/分派器/还原器。您有1个文件,其中封装了用户概念所需的所有内容。为什么这是一种不好的做法?在我看来,它使程序员的生活变得更加轻松,而其他程序员只需从上到下阅读文件即可理解业务逻辑,他们不需要在action/reducer文件之间来回切换。见鬼,甚至不需要
    redux-thunk
    !您甚至可以逐个测试这些功能。因此,可测试性不会丢失。

    首先,在动作创建者中调用
    store.dispatch
    ,它应该返回一个对象(动作),从而简化测试和测试

    您还应该使用而不是
    React.createClass

    回到主题,更专业的动作创作者应该是:

    const setSomeField = value => ({
      type: 'SET_SOME_FIELD',
      value,
    });
    ...
    case 'SET_SOME_FIELD': 
      return { ...state, someField: action.value };
    
    与通用方法相比,此方法的优势 1.更高的可重用性 如果在多个位置设置了
    someField
    ,那么调用
    setSomeField(someValue)
    setState({someField:someValue})}
    更干净

    2.更高的可测试性 您可以轻松地测试
    setSomeField
    ,以确保它仅正确地更改相关状态

    使用泛型
    setState
    ,您也可以测试
    setState({someField:someValue}}
    ,但不能直接保证所有代码都能正确调用它。
    您团队中的某个人可能会输入一个拼写错误,然后调用
    setState({someFeild:someValue})}

    结论 缺点并不明显,因此如果您认为值得为您的项目进行权衡,那么使用通用动作创建者来减少专业动作创建者的数量是完全可以的

    编辑 关于你的建议,将减速机和操作放在同一个文件中:一般来说,最好将它们放在单独的文件中,以便保存;这是一个普遍的原则,不是唯一的反应

    但是,您可以将相关的reducer和action文件放在同一个文件夹中,这可能更好/更差,具体取决于您的项目需求。有关背景信息,请参见和

    您还需要为根目录导出
    userReducer
    const setSomeField = value => ({
      type: 'SET_SOME_FIELD',
      value,
    });
    ...
    case 'SET_SOME_FIELD': 
      return { ...state, someField: action.value };
    
    fetchData()
    fetchUser(id)
    fetchCity(lat, lon)
    
    updateName(name) {
        this.setState({ name })
    }
    
    render() {
        return(<div><ChildComponent onChange={::this.updateName} /></div>)
    }
    
    <button onClick={()=>this.props.onChange('John Doe')}
    
    import { create_store } from 'redux';
    import { create_reducer, redup } from 'redux-decorator';
    
    class State {
        @redup("Todos", "AddTodo", [])
        addTodo(state, action) {
            return [...state, { id: 2 }];
        }
        @redup("Todos", "RemoveTodo", [])
        removeTodo(state, action) {
            console.log("running remove todo");
            const copy = [...state];
            copy.splice(action.index, 1);
            return copy;
        }
    }
    const store = createStore(create_reducer(new State()));
    
    class Note{
            @redup("Notes","AddNote",[])
            addNote(state,action){
                //Code to add a note
            }
        }
        class State{
            aConstant = 1
            @redup("Todos","AddTodo",[])
            addTodo(state,action){
                //Code to add a todo
            }
            note = new Note();
        }
        // create store...
        //Adds a note
        store.dispatch({
            type:'AddNote'
        })
        //Log notes
        console.log(store.getState().note.Notes)
    
    import ActionType from "../actionsEnum.jsx";
    
    const reducer = (state = {
        // Initial state ...
    }, action) => {
        var actionsAllowed = Object.keys(ActionType).map(key => {
            return ActionType[key];
        });
        if (actionsAllowed.includes(action.type) && action.type !== ActionType.NOP) {
            return makeNewState(state, action.state);
        } else {
            return state;
        }
    }
    
    const makeNewState = (oldState, partialState) => {
        var newState = Object.assign({}, oldState);
        const values = Object.values(partialState);
        Object.keys(partialState).forEach((key, ind) => {
            newState[key] = values[ind];
        });
        return newState;
    };
    
    export default reducer;