React + Redux - एक फार्म घटक में CRUD को संभालने का सबसे अच्छा तरीका क्या है?


128

मुझे एक फॉर्म मिला है जिसका उपयोग Create, Read, Update और Delete करने के लिए किया जाता है। मैंने एक ही फॉर्म के साथ 3 घटक बनाए लेकिन मैं उन्हें अलग-अलग सहारा देता हूं। मुझे CreateForm.js, ViewForm.js (डिलीट बटन के साथ आसानी से) और UpdateForm.js मिला।

मैं PHP के साथ काम करता था, इसलिए मैंने हमेशा इन्हें एक रूप में किया।

मैं स्टोर को प्रबंधित करने के लिए रिएक्ट और रेडक्स का उपयोग करता हूं।

जब मैं CreateForm घटक में होता हूं, तो मैं अपने उप-घटकों को पास करता हूं createForm={true}ताकि यह इनपुट मूल्य से भर न जाए और उन्हें अक्षम न करें। मेरे ViewForm घटक में, मैं इस सहारा को पास करता हूं readonly="readonly"

और मुझे एक textarea के साथ एक और समस्या मिली जो एक मूल्य से भरा है और अद्यतन करने योग्य नहीं है। मूल्य के साथ पाठ textarea आसानी से है, लेकिन अद्यतन करने की आवश्यकता है

केवल एक घटक के लिए सबसे अच्छी संरचना क्या है जो फॉर्म के इन विभिन्न राज्यों को संभालती है?

क्या आपके पास साझा करने के लिए कोई सलाह, ट्यूटोरियल, वीडियो, डेमो हैं?

जवाबों:


115

मुझे Redux फॉर्म पैकेज मिला । यह वास्तव में अच्छा काम करता है!

तो, आप Redux का उपयोग React-Redux के साथ कर सकते हैं ।

सबसे पहले आपको एक फॉर्म कंपोनेंट बनाना होगा (जाहिर है):

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

और अपने संयुक्त reducers में redux-form reducer जोड़ें:

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;

और सत्यापनकर्ता मॉड्यूल इस तरह दिखता है:

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

प्रपत्रों को पॉप्युलेट करने का एक और तरीका है, आरंभिक संस्करण सेट करना।

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

यदि आपको इसे संभालने का कोई अन्य तरीका मिला है, तो एक संदेश छोड़ दें! धन्यवाद।


3
बस सोच रहा था - क्या आप अभी भी उपयोग कर रहे हैं redux-forms? मैं सोच रहा हूँ कि प्रतिक्रिया-रूपों की तुलना में वह बॉयलरप्लेट तराजू कैसे है
एशले कूलमैन

2
हाँ, मैं अभी भी इसका उपयोग कर रहा हूँ! वास्तव में अच्छा है, मैंने बहुत बड़े रूप बनाए हैं और इसने # 1 काम किया है। आपको बस अपने घटकों और उनके अद्यतनों के रूप में पास होने के साथ बहुत सावधान रहना होगा। उत्तर की देरी के लिए क्षमा करें।
माइक बाउटिन

1
@ माइकबाउट आप प्रॉप्स के बारे में उस सावधानी के बारे में विस्तार से बता सकते हैं? धन्यवाद
एडम के डीन

यह भी है कि v6.4.3 के रूप में, आप इसे करने के लिए उपयोग कर रहे हैं उनका कहना है लायक पूरी क्षमता है, के प्रदर्शन redux-formहै निराशाजनक एज सहित IE के सभी संस्करणों, पर। यदि आपको इसका समर्थन करना है, तो कहीं और देखें।
स्टीफन कॉलिन्स

2
यह बहुत ही सख्त होना चाहिए, जब आप अपने प्रपत्रों में लैग नहीं बना सकते हैं
माइक बाउटिन

11

अद्यतन: इसके 2018 और मैं ही कभी इस्तेमाल करेंगे Formik (या Formik की तरह पुस्तकालयों)

वहाँ भी है प्रतिक्रिया-redux फार्म ( कदम-दर-कदम ), जिनमें से कुछ का आदान-प्रदान लगता redux फार्म मार्कअप घोषणा के साथ की जावास्क्रिप्ट (और बॉयलरप्लेट)। यह अच्छा लग रहा है, लेकिन मैंने अभी तक इसका उपयोग नहीं किया है।

रीडमे से कट और पेस्ट:

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

संपादित करें: तुलना

प्रतिक्रिया-Redux-form डॉक्स तुलना बनाम redux-form प्रदान करते हैं:

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


4

उन लोगों के लिए जो फॉर्म संबंधित मुद्दों से निपटने के लिए एक विशाल पुस्तकालय की परवाह नहीं करते हैं, मैं Redux-form-utils की सिफारिश करूंगा ।

यह आपके प्रपत्र नियंत्रणों के लिए मूल्य और परिवर्तन हैंडलर उत्पन्न कर सकता है, फॉर्म के रीड्यूसर उत्पन्न कर सकता है, कुछ (या सभी) फ़ील्ड्स को साफ़ करने के लिए एक्शन क्रिएटर्स को संभाल सकता है, आदि।

आपको बस उन्हें अपने कोड में इकट्ठा करना है।

का उपयोग करके 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>
    );
  }
}

हालांकि, इस पुस्तकालय केवल समस्या का हल Cऔर Uके लिए, Rऔर D, हो सकता है एक और अधिक एकीकृत Tableघटक antipate है।


1

उन लोगों के लिए बस एक और बात जो ओवरसाइज़्ड लाइब्रेरी का उपयोग किए बिना पूरी तरह से नियंत्रित फ़ॉर्म घटक बनाना चाहते हैं।

ReduxFormHelper - एक छोटा ES6 वर्ग, 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}
  }
}

यह आपके लिए सभी काम नहीं करता है। हालांकि यह एक नियंत्रित रूप घटक के निर्माण, सत्यापन और हैंडलिंग की सुविधा प्रदान करता है। आप अपनी परियोजना में या इसके बजाय उपरोक्त कोड को कॉपी और पेस्ट कर सकते हैं, संबंधित पुस्तकालय - redux-form-helper(प्लग!) शामिल करें।

कैसे इस्तेमाल करे

पहला चरण Redux राज्य में विशिष्ट डेटा जोड़ रहा है जो हमारे फॉर्म की स्थिति का प्रतिनिधित्व करेगा। इन आंकड़ों में वर्तमान फ़ील्ड मान और साथ ही प्रपत्र में प्रत्येक फ़ील्ड के लिए त्रुटि ध्वज के सेट शामिल होंगे।

प्रपत्र स्थिति को मौजूदा reducer में जोड़ा जा सकता है या एक अलग reducer में परिभाषित किया जा सकता है।

इसके अलावा फॉर्म स्टेट के अद्यतन के साथ-साथ संबंधित एक्शन क्रिएटर के लिए विशेष कार्रवाई को परिभाषित करना आवश्यक है।

क्रिया उदाहरण :

export const FORM_UPDATE = 'FORM_UPDATE' 

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

Reducer उदाहरण :

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

दूसरा और अंतिम चरण हमारे फॉर्म के लिए एक कंटेनर घटक बनाता है और इसे Redux राज्य और क्रियाओं के संबंधित भाग से जोड़ता है।

इसके अलावा, हमें एक फॉर्म मॉडल को परिभाषित करने की आवश्यकता है जो फॉर्म फ़ील्ड की मान्यता को निर्दिष्ट करता है। अब हम ReduxFormHelperघटक के एक सदस्य के रूप में ऑब्जेक्ट को त्वरित करते हैं और वहां हमारे फॉर्म मॉडल और फॉर्म स्टेट की कॉलबैक प्रेषण अपडेट को पास करते हैं।

तब घटक के दशक में render()विधि हम प्रत्येक क्षेत्र के लिए बाध्य करने के लिए है onChangeऔर फ़ॉर्म की onSubmitके साथ घटनाओं processField()और processForm()राज्य में प्रपत्र त्रुटि झंडे के आधार पर प्रत्येक क्षेत्र के लिए तरीके क्रमशः के साथ ही प्रदर्शन त्रुटि ब्लॉक।

नीचे दिया गया उदाहरण ट्विटर बूटस्ट्रैप फ्रेमवर्क से 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)

डेमो

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.