简体   繁体   English

为React可重用组件设计Redux操作和Reducer

[英]Design Redux actions and reducers for a React reusable component

I'm developing a large application with Redux and React and I decided to make some reusable components like custom checkboxes and radio buttons. 我正在使用Redux和React开发一个大型应用程序,我决定制作一些可重用的组件,如自定义复选框和单选按钮。

As I want to store this information into the Redux store to be able to debug state of the application easily and the same component have the same functionality everywhere, it seems a good idea to create reducers and actions for each component. 由于我想将此信息存储到Redux存储中以便能够轻松调试应用程序的状态,并且相同的组件在任何地方都具有相同的功能,因此为每个组件创建reducers和actions似乎是个好主意。

However Redux reducers return a new state and save it in the store with the name of the reducer as a key and this forbids having more than one component of the same type in the same page using the same actions and reducers but keeping different states. 但是Redux reducers返回一个新状态并将其保存在商店中,并将reducer的名称作为键,这禁止在同一页面中使用相同的actions和reducers但在保持不同状态的情况下在同一类型中具有多个相同类型的组件。

I think there are two solutions to this problem: 我认为这个问题有两个解决方案:

  • Create different actions and reducers for each component that I use, even thought the component and it's functionalities are the same. 为我使用的每个组件创建不同的操作和缩减器,甚至认为组件和它的功能是相同的。 This solutions doesn't seem a good solution because there will be a lot of redundant code. 这个解决方案似乎不是一个好的解决方案,因为会有很多冗余代码。

  • Create actions with sufficient parameters to be able to differentiate each one in the reducer and that way change only the part of the state that is specified. 创建具有足够参数的操作,以便能够区分reducer中的每个参数,并且这样只会更改指定状态的一部分。

I went forward with the second option. 我推出了第二个选项。

Actions file for the CheckBox component: CheckBox组件的Actions文件:

import {createAction} from 'redux-actions';

/****** Actions ******/
export const CHANGE_CHECKBOX_STATE = "CHANGE_CHECKBOX_STATE";

/****** Action creators ******/
export const changeCheckboxState = createAction(CHANGE_CHECKBOX_STATE, (block, name, state) => {
  return {
    block,
    name,
    state: {
      checked: state,
    }
  };
});

Reducers file for the CheckBox component: CheckBox组件的Reducers文件:

import {handleActions} from 'redux-actions';

import {CHANGE_CHECKBOX_STATE} from './CheckBox.actions';

export const checkBoxComponent = handleActions({
  CHANGE_CHECKBOX_STATE: (state, action) => ({
    [action.payload.block]: {
      [action.payload.name]: action.payload.state
    }
  })
}, {});

I use block to specify the page, name to specify the name of the specific component (eg gender) and state as and object with the new state. 我使用block来指定页面,使用name来指定特定组件的名称(例如性别),使用新状态作为对象和状态。

But this solution has some problems too: 但是这个解决方案也存在一些问题:

  • Can't specify initial state of each component because the keys of the state are dynamic. 无法指定每个组件的初始状态,因为状态的键是动态的。
  • Complicates to much the store structure with a lot of nested states. 使具有大量嵌套状态的商店结构变得复杂。
  • Aggregates data by component and not by form which will complicate debugging and it's logically incorrect. 按组件聚合数据,而不是按表单聚合,这会使调试复杂化并且逻辑上不正确。

I don't have enough experience with Redux and React to think of a better solution to this problem. 我没有足够的经验使用Redux和React来考虑更好地解决这个问题。 But it seems to me that I'm missing something important about React-Redux relationship and this raises some questions: 但在我看来,我遗漏了一些关于React-Redux关系的重要内容,这引发了一些问题:

  • Is it a good idea to store state of this reusable components into the Redux store? 将这个可重用组件的状态存储到Redux存储中是一个好主意吗?

  • Am I wrong in binding together React components with Redux actions and reducers? 将React组件与Redux操作和Reducer绑定在一起是错误的吗?

Don't describe your Redux reducers (your overall application state) in terms of the components that consume that state. 不要根据使用该状态的组件描述Redux Reducer(您的整体应用程序状态)。 Track it the other way, describing your application state in a descriptive manner and then have your "dumb" components consume that descriptive state. 以另一种方式跟踪它,以描述性方式描述您的应用程序状态,然后让您的“哑”组件消耗该描述性状态。

Rather than tracking "The state of this checkbox for the form related to foo", track "this boolean value of foo" and then have the checkbox consume that state. 而不是跟踪“与foo相关的表单的此复选框的状态”,跟踪“foo的此布尔值”,然后让复选框使用该状态。

An example store for the checkbox: 复选框的示例商店:

const initialState = {
    someFooItem: { isCertainType: false }
};

export function foos(state = initialState, action) {
    switch(action.type){
        case(UPDATE_FOO_VALUE):
            return {
                ...state, 
                [action.payload.id]: {
                    isCertainType: action.payload.isCertainType
                }
            }
    }
}

An example checkbox consuming the store 使用商店的示例复选框

class CheckBox extends React.Component {
    render() {
        return <input type="checkbox" checked={this.props.checked} />
    }
}

The parent component 父组件

class ParentComponent extends React.Component {
     render() {
         return <CheckBox checked={this.foo.isCertainType} />
     }
}

The fundamental mistake here is that you have 0 dumb components. 这里的根本错误是你有0个哑组件。 Basic controls like checkboxes should be dumb components using just props, their parent having the responsibility to select and update the proper piece of state for a particular checkbox. 像复选框这样的基本控件应该是只使用道具的哑组件,它们的父级负责选择和更新特定复选框的正确状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM