React + Redux – 在表单组件中处理CRUD的最佳方法是什么?

我有一个用于创build,读取,更新和删除的表单。 我用相同的forms创build了3个组件,但我传递了不同的道具。 我得到了CreateForm.js,ViewForm.js(只读删除button)和UpdateForm.js。

我曾经使用PHP,所以我总是以一种forms做到这一点。

我使用React和Redux来pipe理商店。

当我在CreateForm组件中时,我传递给我的子组件这个道具createForm={true}不填充input值,并且createForm={true}用它们。 在我的ViewForm组件中,我通过这个道具readonly="readonly"

而且我还遇到了一个textarea问题,这个textarea充满了价值,而且不可更新。 反应textarea的值是只读的,但需要更新

什么是最好的结构只有一个组件处理这些forms的不同状态?

你有任何build议,教程,video,演示分享?

我find了Redux Form包。 它做得非常好!

所以,你可以使用Redux和React-Redux 。

首先,你必须创build一个表单组件(显然):

 import React from 'react'; import { reduxForm } from 'redux-form'; import validateContact from '../utils/validateContact'; class ContactForm extends React.Component { render() { const { fields: {name, address, phone}, handleSubmit } = this.props; return ( <form onSubmit={handleSubmit}> <label>Name</label> <input type="text" {...name}/> {name.error && name.touched && <div>{name.error}</div>} <label>Address</label> <input type="text" {...address} /> {address.error && address.touched && <div>{address.error}</div>} <label>Phone</label> <input type="text" {...phone}/> {phone.error && phone.touched && <div>{phone.error}</div>} <button onClick={handleSubmit}>Submit</button> </form> ); } } ContactForm = reduxForm({ form: 'contact', // the name of your form and the key to // where your form's state will be mounted fields: ['name', 'address', 'phone'], // a list of all your fields in your form validate: validateContact // a synchronous validation function })(ContactForm); export default ContactForm; 

在此之后,您连接处理表单的组件:

 import React from 'react'; import { connect } from 'react-redux'; import { initialize } from 'redux-form'; import ContactForm from './ContactForm.react'; class App extends React.Component { handleSubmit(data) { console.log('Submission received!', data); this.props.dispatch(initialize('contact', {})); // clear form } render() { return ( <div id="app"> <h1>App</h1> <ContactForm onSubmit={this.handleSubmit.bind(this)}/> </div> ); } } export default connect()(App); 

在你的组合减速器中添加减速器减速器:

 import { combineReducers } from 'redux'; import { appReducer } from './app-reducers'; import { reducer as formReducer } from 'redux-form'; let reducers = combineReducers({ appReducer, form: formReducer // this is the form reducer }); export default reducers; 

validation器模块如下所示:

 export default function validateContact(data, props) { const errors = {}; if(!data.name) { errors.name = 'Required'; } if(data.address && data.address.length > 50) { errors.address = 'Must be fewer than 50 characters'; } if(!data.phone) { errors.phone = 'Required'; } else if(!/\d{3}-\d{3}-\d{4}/.test(data.phone)) { errors.phone = 'Phone must match the form "999-999-9999"' } return errors; } 

表单完成后,当你想用一些值填充所有字段时,可以使用initialize函数:

 componentWillMount() { this.props.dispatch(initialize('contact', { name: 'test' }, ['name', 'address', 'phone'])); } 

填充表单的另一种方法是设置initialValues。

 ContactForm = reduxForm({ form: 'contact', // the name of your form and the key to fields: ['name', 'address', 'phone'], // a list of all your fields in your form validate: validateContact // a synchronous validation function }, state => ({ initialValues: { name: state.user.name, address: state.user.address, phone: state.user.phone, }, }))(ContactForm); 

如果你有其他办法来处理这个,只要留言! 谢谢。

还有一种反应 – 还原forms ( step-by-step ),它似乎用标记声明来交换一些redux-form的javascript(&boilerplate)。 它看起来不错,但我还没有使用它。

自述文件中的剪切和粘贴:

 import React from 'react'; import { createStore, combineReducers } from 'redux'; import { Provider } from 'react-redux'; import { modelReducer, formReducer } from 'react-redux-form'; import MyForm from './components/my-form-component'; const store = createStore(combineReducers({ user: modelReducer('user', { name: '' }), userForm: formReducer('user') })); class App extends React.Component { render() { return ( <Provider store={ store }> <MyForm /> </Provider> ); } } 

./components/my-form-component.js

 import React from 'react'; import { connect } from 'react-redux'; import { Field, Form } from 'react-redux-form'; class MyForm extends React.Component { handleSubmit(val) { // Do anything you want with the form value console.log(val); } render() { let { user } = this.props; return ( <Form model="user" onSubmit={(val) => this.handleSubmit(val)}> <h1>Hello, { user.name }!</h1> <Field model="user.name"> <input type="text" /> </Field> <button>Submit!</button> </Form> ); } } export default connect(state => ({ user: state.user }))(MyForm); 

编辑:比较

反应还原forms的文档提供了一个比较vs还原forms:

https://davidkpiano.github.io/react-redux-form/docs/guides/compare-redux-form.html

对于那些不关心处理表单相关问题的庞大图书馆的人,我会推荐使用redux-form-utils 。

它可以为您的表单控件生成值和更改处理程序,生成表单的缩减器,方便的动作创build者清除某些(或全部)字段等。

所有你需要做的就是把它们组装到你的代码中。

通过使用redux-form-utils ,你最终将得到如下的表单操作:

 import { createForm } from 'redux-form-utils'; @createForm({ form: 'my-form', fields: ['name', 'address', 'gender'] }) class Form extends React.Component { render() { const { name, address, gender } = this.props.fields; return ( <form className="form"> <input name="name" {...name} /> <input name="address" {...address} /> <select {...gender}> <option value="male" /> <option value="female" /> </select> </form> ); } } 

但是,这个库只能解决问题CU ,对于RD ,也许一个更加集成的Table组件是反对的。

对于那些想要创build完全控制的表单组件而不使用超大型库的用户来说,还有一件事情。

ReduxFormHelper – 一个小的ES6类,less于100行:

 class ReduxFormHelper { constructor(props = {}) { let {formModel, onUpdateForm} = props this.props = typeof formModel === 'object' && typeof onUpdateForm === 'function' && {formModel, onUpdateForm} } resetForm (defaults = {}) { if (!this.props) return false let {formModel, onUpdateForm} = this.props let data = {}, errors = {_flag: false} for (let name in formModel) { data[name] = name in defaults? defaults[name] : ('default' in formModel[name]? formModel[name].default : '') errors[name] = false } onUpdateForm(data, errors) } processField (event) { if (!this.props || !event.target) return false let {formModel, onUpdateForm} = this.props let {name, value, error, within} = this._processField(event.target, formModel) let data = {}, errors = {_flag: false} if (name) { value !== false && within && (data[name] = value) errors[name] = error } onUpdateForm(data, errors) return !error && data } processForm (event) { if (!this.props || !event.target) return false let form = event.target if (!form || !form.elements) return false let fields = form.elements let {formModel, onUpdateForm} = this.props let data = {}, errors = {}, ret = {}, flag = false for (let n = fields.length, i = 0; i < n; i++) { let {name, value, error, within} = this._processField(fields[i], formModel) if (name) { value !== false && within && (data[name] = value) value !== false && !error && (ret[name] = value) errors[name] = error error && (flag = true) } } errors._flag = flag onUpdateForm(data, errors) return !flag && ret } _processField (field, formModel) { if (!field || !field.name || !('value' in field)) return {name: false, value: false, error: false, within: false} let name = field.name let value = field.value if (!formModel || !formModel[name]) return {name, value, error: false, within: false} let model = formModel[name] if (model.required && value === '') return {name, value, error: 'missing', within: true} if (model.validate && value !== '') { let fn = model.validate if (typeof fn === 'function' && !fn(value)) return {name, value, error: 'invalid', within: true} } if (model.numeric && isNaN(value = Number(value))) return {name, value: 0, error: 'invalid', within: true} return {name, value, error: false, within: true} } } 

它并没有为你做所有的工作。 但是它有助于创build,validation和处理受控表单组件。 您可以将上面的代码复制并粘贴到您的项目中,或者相反,包含相应的库 – redux-form-helper (plug!)。

如何使用

第一步是将特定的数据添加到Redux状态,这将代表我们的表单的状态。 这些数据将包括当前字段值以及表单中每个字段的错误标志集。

表格状态可以被添加到现有的减速器中或者在单独的减速器中定义。

此外,有必要定义启动表单状态更新的具体操作以及相应的动作创build者。

行动举例

 export const FORM_UPDATE = 'FORM_UPDATE' export const doFormUpdate = (data, errors) => { return { type: FORM_UPDATE, data, errors } } ... 

减速器例子

 ... const initialState = { formData: { field1: '', ... }, formErrors: { }, ... } export default function reducer (state = initialState, action) { switch (action.type) { case FORM_UPDATE: return { ...ret, formData: Object.assign({}, formData, action.data || {}), formErrors: Object.assign({}, formErrors, action.errors || {}) } ... } } 

第二步也是最后一步是为我们的表单创build一个容器组件,并将它与Redux状态和动作的相应部分连接起来。

我们还需要定义一个表单模型来指定表单字段的validation。 现在我们将ReduxFormHelper对象实例ReduxFormHelper组件的成员,并传递我们的表单模型和表单状态的callback调度更新。

然后,在组件的render()方法中,我们必须分别将每个字段的onChange和表单的onSubmit事件与processField()processForm()方法绑定,并根据状态中的表单错误标志显示每个字段的错误块。

下面的例子使用来自Twitter Bootstrap框架的CSS。

容器组件示例

 import React, {Component} from 'react'; import {connect} from 'react-redux' import ReduxFormHelper from 'redux-form-helper' class MyForm extends Component { constructor(props) { super(props); this.helper = new ReduxFormHelper(props) this.helper.resetForm(); } onChange(e) { this.helper.processField(e) } onSubmit(e) { e.preventDefault() let {onSubmitForm} = this.props let ret = this.helper.processForm(e) ret && onSubmitForm(ret) } render() { let {formData, formErrors} = this.props return ( <div> {!!formErrors._flag && <div className="alert" role="alert"> Form has one or more errors. </div> } <form onSubmit={this.onSubmit.bind(this)} > <div className={'form-group' + (formErrors['field1']? ' has-error': '')}> <label>Field 1 *</label> <input type="text" name="field1" value={formData.field1} onChange={this.onChange.bind(this)} className="form-control" /> {!!formErrors['field1'] && <span className="help-block"> {formErrors['field1'] === 'invalid'? 'Must be a string of 2-50 characters' : 'Required field'} </span> } </div> ... <button type="submit" className="btn btn-default">Submit</button> </form> </div> ) } } const formModel = { field1: { required: true, validate: (value) => value.length >= 2 && value.length <= 50 }, ... } function mapStateToProps (state) { return { formData: state.formData, formErrors: state.formErrors, formModel } } function mapDispatchToProps (dispatch) { return { onUpdateForm: (data, errors) => { dispatch(doFormUpdate(data, errors)) }, onSubmitForm: (data) => { // dispatch some action which somehow updates state with form data } } } export default connect(mapStateToProps, mapDispatchToProps)(MyForm) 

演示