如果有一个用户列表,每个条目都有一个按钮»编辑«。如果用户点击它,则会发生以下情况:
<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
})}
);
}
}
所以我的问题:这是一个好主意吗?如果是,那么进行清理的正确方法是什么,所以当在组件的生命周期中我应该从状态中删除数据?还有另一种方法可以做到这一点,这更容易吗?
感谢您的帮助!
答案 0 :(得分: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);
您的ListComponent
render() {
return <ul>
{this.props.users.map(user =>
<li key={user.id}>
...
<UserEditForm formId={'form-' + user.id} onSubmit={...} />
</li>
)}
</ul>
}
这允许您拥有动态表单名称。
答案 1 :(得分:0)
即使@jpdelatorre的答案对我来说似乎是最受欢迎的,因为它还包含了redux-forms的链接,这可能对我有很大的帮助,我想在这里发布我的工作解决方案,只是在有人可能会发现它很有用。它只是打了我一夜,所以需要测试我的想法是否正确,我最终可以证明。
我无法使用唯一的HOC进行整个映射,我也需要添加/修改Reducer。基本上它的工作原理如下:
数据映射由ID
包装原始动作创建者,以便将使用的ID附加到对象
减速器被包裹两个并由»datamapped«reducer
因此原始Reducer和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, {});