Programming

React + Redux-양식 구성 요소에서 CRUD를 처리하는 가장 좋은 방법은 무엇입니까?

procodes 2020. 7. 6. 21:45
반응형

React + Redux-양식 구성 요소에서 CRUD를 처리하는 가장 좋은 방법은 무엇입니까?


작성, 읽기, 업데이트 및 삭제에 사용되는 양식이 하나 있습니다. 같은 형식으로 3 개의 구성 요소를 만들었지 만 다른 소품을 전달합니다. CreateForm.js, ViewForm.js (삭제 버튼으로 읽기 전용) 및 UpdateForm.js가 있습니다.

예전에는 PHP로 작업 했었으므로 항상 한 가지 형태로 수행했습니다.

상점을 관리하기 위해 React와 Redux를 사용합니다.

CreateForm 구성 요소에있을 때 하위 구성 요소로 전달 createForm={true}하여 입력으로 값을 채우지 않고 비활성화하지 않도록 소품 전달 합니다. 내 ViewForm 구성 요소 에서이 소품을 전달합니다 readonly="readonly".

그리고 나는 값이 가득하고 업데이트 할 수없는 텍스트 영역에 또 다른 문제가 있습니다. 값이있는 반응 텍스트 영역은 읽기 전용이지만 업데이트해야합니다

이러한 다양한 형태의 양식을 처리하는 하나의 구성 요소 만 갖는 가장 좋은 구조는 무엇입니까?

조언, 튜토리얼, 비디오, 공유 할 데모가 있습니까?


Redux Form 패키지를 찾았습니다 . 정말 잘해!

따라서 Reux-Redux 와 함께 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);

그리고 결합 된 감속기에 redux- 폼 감속기를 추가하십시오.

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

양식을 채우는 또 다른 방법은 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);

이 문제를 처리 할 다른 방법이 있다면 메시지를 남겨주세요! 감사합니다.


업데이트 : 2018 년에는 Formik (또는 Formik와 같은 라이브러리) 만 사용합니다.

또한 redux-form 의 자바 스크립트 (및 상용구)를 마크 업 선언과 교환하는 것으로 보이는 react-redux-form ( 단계별 )도 있습니다. 좋아 보이지만 아직 사용하지 않았습니다.

읽어보기에서 잘라내어 붙여 넣기 :

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

편집 : 비교

반응 리덕스 양식 문서는 리덕스 양식과 비교합니다.

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


양식 관련 문제를 처리하기 위해 거대한 라이브러리를 신경 쓰지 않는 사람들에게는 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하는 것입니다.


특 대형 라이브러리를 사용하지 않고 완전히 제어 된 양식 구성 요소를 작성하려는 사람들에게는 또 다른 것입니다.

ReduxFormHelper -100 줄 미만의 작은 ES6 클래스 :

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 상태에 추가하는 것입니다. 이 데이터에는 양식의 각 필드에 대한 현재 플래그 값과 오류 플래그 세트가 포함됩니다.

폼 상태는 기존 감속기에 추가되거나 별도의 감속기에 정의 될 수 있습니다.

또한 양식 작성 및 각 조치 작성자의 업데이트를 시작하는 특정 조치를 정의해야합니다.

액션 예 :

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

The second and final step is create a container component for our form and connect it with respective part of Redux state and actions.

Also we need to define a form model specifying validation of form fields. Now we instantiate ReduxFormHelper object as a member of the component and pass there our form model and a callback dispatching update of the form state.

Then in the component's render() method we have to bind each field's onChange and the form's onSubmit events with processField() and processForm() methods respectively as well as display error blocks for each field depending on the form error flags in the state.

The example below uses CSS from Twitter Bootstrap framework.

Container Component example:

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

참고URL : https://stackoverflow.com/questions/33237818/react-redux-whats-the-best-way-to-handle-crud-in-a-form-component

반응형