【问题标题】:React/Redux—Individual state for each Instance of a ComponentReact/Redux——组件的每个实例的独立状态
【发布时间】:2016-11-23 10:31:44
【问题描述】:

如果有一个用户列表并且每个条目都有一个按钮 »EDIT«。如果用户点击它会发生以下情况:

  1. 向服务器请求表单
  2. 将组件<UserEditForm />添加到条目中,扩展条目的内容

除了一件事之外,这很好用:如果单击更多按钮,则表单的每个实例都会收到请求的最后一个用户表单的数据。那是因为我在该州只有一个userform 属性。

为了解决这个问题,我想将userform 交换为userforms,它应该/可能是这样的对象:

userforms: {
  <id of first instance>: { … }, //formdata
  <id of second instance>: { … },
  …
}

但由于我是 React/Redux 的新手,我真的不知道该怎么做,也不知道真正做到这一点的“正确”方法或最佳实践是什么。

我的想法是像这样创建一个更高阶的组件:

import React from 'react';
import {connect} from 'react-redux';
import {uuid} from '../../helpers/uuid';

export const formdatainstance = (FormInstance) => {

  let id = null;

  class FormDataMapper extends React.Component {
    constructor (props) {
      super(props);
      id = uuid();
    }

    render () {
      //extract the formdata from the state
      //using the id
      return <FormInstance { ...this.props } />
    }
  }

  const mapStateToProps = (state) => {
    console.log(id); //is null for one run
    return {
      userforms: state.userforms
    };
  };

  return connect(mapStateToProps)(FormDataMapper);
}

所以在 List 组件中我可以:

import UserEditForm from './UserEditForm';
import {formdatainstance} from './formdatainstance';

const MappedUserEditForm = formdatainstance(UserEditForm);

class List extends React.Component {
  render(){
    return (
      {users.map(user => {
        //more stuff
        <MappedUserEditForm />
        //more stuff
      })}
    );
  }
}

所以我的问题:这是一个好主意吗?如果是,那么进行清理的正确方法是什么,那么在组件的生命周期中,我应该在何时从状态中删除数据?还有其他更简单的方法吗?

感谢您的帮助!

【问题讨论】:

  • 您想在状态中保留多个表单数据(针对每个用户)吗?或者你有一个表单数据的状态对象就足够了,它由用户填充并在你为另一个用户单击编辑时重置?你也可以从你的减速器中发布相关代码吗?
  • 我想为渲染表单的每个实例提供唯一的表单数据。因此,如果列表中有三个用户,我想最终得到三个表单,显示每个用户的数据,可以单独编辑和提交。
  • 我也很乐意抽象这个功能,以便它可以应用于代表系统中其他实体的另一种形式的其他组件。

标签: reactjs redux react-redux redux-form


【解决方案1】:

这是你可以做的......

import React from 'react';
import { compose } from 'redux';
import { connect } from 'react-redux';
import { reduxForm } from 'redux-form';

class UserEditForm extends Component {
   ...

   render() {
      return <form onSubmit={this.props.handleSubmit(this.props.onSubmit)}>
          ...form fields
      </form>
   }
}

const mapStateToProps = (state, ownProps) => {
   return {
      form: ownProps.formId
   }
}

export default compose(
   connect(mapStateToProps),
   reduxForm({
      //...other redux-form options
   })
)(UserEditForm);

你的列表组件

render() {
   return <ul>
      {this.props.users.map(user => 
         <li key={user.id}>
             ...
             <UserEditForm formId={'form-' + user.id} onSubmit={...} />
         </li>
      )}
   </ul>
}

这允许您拥有一个动态的表单名称。

【讨论】:

  • 感谢您的回复!我想我会看看 redux 表单!但是您发布的内容似乎很有趣,要检查一下……
【解决方案2】:

即使@jpdelatorre 的答案对我来说似乎是最好的选择,因为它还包含指向 redux-forms 的链接,这可能会对我有很大帮助,我想在这里发布我的工作解决方案,就在情况下有人可能会发现它很有用。我一夜之间就被它击中了,所以需要测试我的想法是否正确,我最终能证明什么。

我无法使用唯一的 HOC 完成整个映射,我还需要添加/修改减速器。基本上它是这样工作的:

  1. 数据映射由ID完成,

  2. 原始动作创建者被包装,这样使用的 id 被附加到对象上

  3. reducer 被包装了两个并由 »datamapped« reducer 调用

所以原始reducers 和action creators 的代码不需要更改,这使得包装类易于使用。我最初想使用动态创建的uuid,但我放弃了它,以便保存和恢复整个应用程序状态。

所以 HOC 代码是:

import React from 'react';
import {connect} from 'react-redux';

// The Component to wrap,
// all of its actions
// its default state
export const formdatainstance = (FormInstance, Actions, defaultState = {}) => {

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

  class FormDataMapper extends React.Component {
    static propTypes = {
      id: React.PropTypes.string.isRequired
    };

    static contextTypes = {
      store: React.PropTypes.object
    };

    //most of mapping happens here
    render () {
      //wrap the action creators
      const actions = Object.keys(Actions).reduce((list, key) =>{
        list[key] = (...args) => {
          const action = Actions[key](...args);

          //handle asyn operations as well
          if('then' in action && typeof action['then'] == 'function') {
            action.then(data => {
              //attaching the id
              this.props.dispatch({...data, id: this.props.id});
            });
          } else {
            //attach the id
            this.context.store.dispatch({...action, id: this.props.id });
          }
        };
        return list;
      }, {}),
        //there wont be any data at first, so the default state is handed
        //over
        mappedProps = this.props.mappedData.hasOwnProperty(this.props.id) ?
          this.props.mappedData[this.props.id] : defaultState;

      //merge the hotchpotch
      let props = Object.assign({}, mappedProps, this.props, actions);

      //clean up
      delete props.id;
      delete props.mappedData;

      return <FormInstance { ...props } />
    }
  }

  return connect(mapStateToProps)(FormDataMapper);
};

reducer 代码:

//hlper method
export const createTypesToReducerMap = (types, reducer) => {
  return Object.keys(types).reduce((map, key) => {
    map[types[key]] = reducer;
    return map;
  }, {});
}


export const createMappedReducer = (reducerMap, defaultState = {}) => {
  const HANDLERS = reducerMap.reduce((handlers, typeMap) => {
    return { ...handlers, ...typeMap };
  },{});

  return (state, action) => {

    if (!action.hasOwnProperty('id')) {
      if (state === undefined) return defaultState;
      return state;
    }

    const reducer = HANDLERS.hasOwnProperty(action.type) ?
          HANDLERS[action.type] : null;

    let a = {...action};
    delete a.id;


    return reducer !== null ?
        Object.assign({}, state, { [action.id]: reducer(state[action.id], a)}) :
        state;
  }
}

最后是商店:

const userEditTypeReducerMap = createTypesToReducerMap(userEditTypes, userFormReducer);


const reducer = combineReducers({
  …
  mappedData: createMappedReducer(
    [userEditTypeReducerMap], {})
  …
});


export default compose(
  applyMiddleware(
    thunk
  )
)(createStore)(reducer, {});

【讨论】:

    猜你喜欢
    • 2018-08-05
    • 2018-05-01
    • 2021-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-17
    • 2016-08-08
    • 2020-12-21
    相关资源
    最近更新 更多