【问题标题】:Sharing actions and reducers with react/redux使用 react/redux 共享 action 和 reducer
【发布时间】:2015-11-13 20:41:25
【问题描述】:

我仍然是一个 react/redux 新手。在一个页面上,我有大量的文本输入。过了一会儿,我开始注意到我的操作文件具有执行相同操作但输入不同的函数:

export function setInputName(string) {
  return {
    type: 'SET_CURRENT_NAME',
    payload: {value: string}
  };
}
export function setInputCity(string) {
  return {
    type: 'SET_CURRENT_CITY',
    payload: {value: string}
  };
}

我的减速器看起来像:

export function currentName(state='', {type, payload}) {
  switch (type) {
  case 'SET_CURRENT_NAME':
    return payload.value;
  default:
    return state;
  }
}
export function currentCity(state='', {type, payload}) {
  switch (type) {
  case 'SET_CURRENT_CITY':
    return payload.value;
  default:
    return state;
  }
}

我的组件有以下多个输入:

import {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {setInputName, setInputCity} from 'actions/form';

export default class Form extends Component {
  static propTypes = {
    setInputName: PropTypes.func.isRequired,
    setInputCity: PropTypes.func.isRequired,
    currentName: PropTypes.string.isRequired,
    currentCity: PropTypes.string.isRequired
  }
  render() {
    let {setInputName, setInputCity, currentName, currentCity} = this.props;
    return (
      <div>
        <input
          type="text" 
          placeholder="Name"
          onChange={(e) => setInputName(e.currentTarget.value)}
          value={currentName}
        />
        <input
          type="text" 
          placeholder="City"
          onChange={(e) => setInputCity(e.currentTarget.value)}
          value={currentCity}
        />
      </div>
    );
  }
}

function select(state) {
  return {
    currentName: state.form.currentName,
    currentCity: state.form.currentCity
  };
}

function actions(dispatch) {
  return bindActionCreators({
   setInputName: setInputName,
   setInputCity: setInputCity,
  }, dispatch);
}

export default connect(select, actions)(Form);

这不是很干燥,我立即认为我做错了什么。有没有一种好方法可以为我的应用程序的每个页面和组件上的所有文本输入提供一个通用的 setInputValue 操作?我还想为每个输入使用一个通用的减速器。谢谢。

更新

这是我的粗略示例,但我觉得这仍然有点令人费解,必须有更好的方法。基本上在减速器中,我检查该输入是否已被使用并添加到状态中。如果没有,我添加它。如果是这样,我只是更新它的值。 编辑我撒谎这实际上不起作用。

// actions
export function updateTextInput(name, value) {
  return {
    type: 'SET_CURRENT_TEXT_INPUT',
    payload: {name: name, value: value}
  };
}

// reducer
export function currentTextInput(state=[], {type, payload}) {
  switch (type) {
  case 'SET_CURRENT_TEXT_INPUT':
    let newState = state;
    let curInput = state.findIndex(function(elem) {
      return elem.name === payload.name;
    });
    if (curInput === -1) {
      newState.push({name: payload.name, value: payload.value});
    } else {
      newState[curInput].value = payload.value;
    }
    return newState;
  default:
    return state;
  }
}

// component
...
render() {
  let {updateTextInput, currentTextInput} = this.props;
  return (
    <div>
      <input
        type="text" 
        placeholder="Name"
        onChange={(e) => updateTextInput('name', e.currentTarget.value)}
        value={currentTextInput.name}
      />
      <input
        type="text" 
        placeholder="City"
        onChange={(e) => updateTextInput('city', e.currentTarget.value)}
        value={currentTextInput.city}
      />
    </div>
  );
}
...

【问题讨论】:

    标签: javascript reactjs redux


    【解决方案1】:

    简化代码的第一步是使用高阶化简器/函数。

    function makePropReducer(actionType, prop) {
      return (state = '', {type, payload}) => {
        if (type === actionType) {
          return payload[prop];
        }
        return state;
      };
    }
    
    export const currentName = makePropReducer('SET_CURRENT_NAME', 'value');
    export const currentCity = makePropReducer('SET_CURRENT_CITY', 'value');
    

    【讨论】:

    • 这太棒了。肯定用这个。谢谢
    【解决方案2】:

    好的,这是一个应要求的示例。假设我有一个用户数据模型,它看起来像这样:

    {
      id: 1,
      name: 'Some Name',
      email: 'someemail@some.com',
      age: 21
    }
    

    按照您当前的设置方式,您必须执行 SET_USER_NAME、SET_USER_EMAIL、SET_USER_AGE 操作。当您可以拥有一个接收更新值对象作为参数的 UPDATE_USER 操作时,不需要所有这些。

    要为第一个用户设置新名称,您可以调用 UPDATE_USER 操作,例如:

    updateUser(1, { name: 'New Name' })

    在用户化简器中,您将更新用户的状态(用户数组),例如:

    function updateUser(usersState, id, attrs) {
      return usersState.map(user =>
        user.id === id ?
        Object.assign({}, user, attrs) :
        user
      );
    }
    

    【讨论】:

    • 这说明了很多事情。如果我在一个页面上有多个不相关的表单并且没有一个特定的模型可以使用怎么办?我还能以某种方式使用通用动作/减速器吗?整个页面没有一个巨大的模型。
    • 每个模型都应该有自己的reducer来接收自己的状态切片。但是,您可以对可以导入并在各种减速器中使用的单个更新函数
    【解决方案3】:

    我最近想出了这样的动作,

    export function controlStateChange(controlName, propertyName, value) {
      return { type: 'CONTROL_STATE_CHANGE', payload: { controlName, propertyName, value } };
    }
    

    以及对应的reducer,

    const actionHandlers = {
      'CONTROL_STATE_CHANGE': (state, payload) => {
        const controls = {
          [payload.controlName]: {
            [payload.propertyName]: payload.value
          }
        };
    
        return {...state, controls };
      }
    };
    

    很笼统,希望也能满足你的需要。

    【讨论】:

      【解决方案4】:

      要将输入保存到状态,您可以使用redux-form

      您的问题似乎与共享减速器/动作无关。 分享actions/reducers,我写了一个,大家可以看看redux-conditional

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-05-16
        • 2023-03-22
        • 2018-11-20
        • 1970-01-01
        • 1970-01-01
        • 2018-11-08
        • 2016-08-16
        • 2019-02-21
        相关资源
        最近更新 更多