tutorial react javascript reactjs crud redux

javascript - tutorial - react crud generator



React+Redux: ¿cuál es la mejor manera de manejar CRUD en un componente de formulario? (4)

ACTUALIZACIÓN: es 2018 y solo Formik (o bibliotecas similares a Formik)

También hay react-redux-form ( step-by-step ), que parece intercambiar parte del javascript de redux-form (& boilerplate) con la declaración de marcado. Se ve bien, pero aún no lo he usado.

Un cortar y pegar del archivo Léame:

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);

Editar: Comparación

Los documentos react-redux-form proporcionan una comparación frente a la forma redux:

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

Tengo un formulario que está acostumbrado a Crear, Leer, Actualizar y Eliminar. Creé 3 componentes con la misma forma pero les paso diferentes accesorios. Obtuve CreateForm.js, ViewForm.js (solo lectura con el botón Eliminar) y UpdateForm.js.

Solía ​​trabajar con PHP, así que siempre hacía esto de una forma.

Uso React y Redux para administrar la tienda.

Cuando estoy en el componente CreateForm, paso a mis subcomponentes estos accesorios createForm={true} para no llenar las entradas con un valor y no deshabilitarlas. En mi componente ViewForm, paso estos accesorios readonly="readonly" .

Y tengo otro problema con un área de texto que está lleno de un valor y no es actualizable. Reaccionar el área de texto con valor es de solo lectura, pero debe actualizarse

¿Cuál es la mejor estructura para tener un solo componente que maneje estos diferentes estados del formulario?

¿Tienes algún consejo, tutoriales, videos, demos para compartir?


Encontré el paquete Redux Form . ¡Hace un muy buen trabajo!

Entonces, puedes usar Redux con React-Redux .

Primero tienes que crear un componente de formulario (obviamente):

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;

Después de esto, conecta el componente que maneja el formulario:

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);

Y agregue el reductor de forma redux en sus reductores combinados:

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;

Y el módulo validador se ve así:

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; }

Después de completar el formulario, cuando desee completar todos los campos con algunos valores, puede usar la función de initialize :

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

Otra forma de llenar los formularios es establecer los valores iniciales.

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);

Si tiene otra forma de manejar esto, ¡solo deje un mensaje! Gracias.


Para aquellos a quienes no les importa una enorme biblioteca para manejar problemas relacionados con formularios, recomendaría redux-form-utils .

Puede generar valor y cambiar manejadores para sus controles de formulario, generar reductores del formulario, creadores de acciones útiles para borrar ciertos (o todos) campos, etc.

Todo lo que necesitas hacer es ensamblarlos en tu código.

Al usar redux-form-utils , terminas con una manipulación de formularios como la siguiente:

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> ); } }

Sin embargo, esta biblioteca solo resuelve el problema C y U , para R y D , quizás un componente de Table más integrado sea antipate.


Solo otra cosa para aquellos que desean crear un componente de formulario totalmente controlado sin usar una biblioteca de gran tamaño.

ReduxFormHelper : una pequeña clase ES6, menos de 100 líneas:

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} } }

No hace todo el trabajo por ti. Sin embargo, facilita la creación, validación y manejo de un componente de formulario controlado. Puede copiar y pegar el código anterior en su proyecto o, en su lugar, incluir la biblioteca respectiva - redux-form-helper (plug!).

Cómo utilizar

El primer paso es agregar datos específicos al estado de Redux que representarán el estado de nuestro formulario. Estos datos incluirán valores de campo actuales, así como un conjunto de indicadores de error para cada campo en el formulario.

El estado del formulario puede agregarse a un reductor existente o definirse en un reductor separado.

Además, es necesario definir acciones específicas que inicien la actualización del estado del formulario, así como el creador de acciones respectivo.

Ejemplo de acción :

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

Ejemplo reductor :

... 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 || {}) } ... } }

El segundo y último paso es crear un componente contenedor para nuestro formulario y conectarlo con la parte respectiva del estado y las acciones de Redux.

También necesitamos definir un modelo de formulario que especifique la validación de los campos de formulario. Ahora instanciamos el objeto ReduxFormHelper como miembro del componente y pasamos nuestro modelo de formulario y una actualización de envío de devolución de llamada del estado del formulario.

Luego, en el método render() del componente, debemos vincular onChange cada campo y los eventos onSubmit del formulario con los onSubmit processField() y processForm() respectivamente, así como mostrar bloques de error para cada campo según los indicadores de error de formulario en el estado.

El siguiente ejemplo utiliza CSS del marco de Twitter Bootstrap.

Ejemplo de componente de contenedor :

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)

Demo