sourcecode

React + Redux - 폼 컴포넌트에서 CRUD를 처리하는 가장 좋은 방법은 무엇입니까?

codebag 2023. 3. 29. 21:26
반응형

React + Redux - 폼 컴포넌트에서 CRUD를 처리하는 가장 좋은 방법은 무엇입니까?

작성, 읽기, 업데이트 및 삭제에 익숙한 폼이 하나 있습니다.같은 형태로 3개의 컴포넌트를 만들었지만 다른 소품을 전달합니다.CreateForm.js, ViewForm.js(삭제버튼을 사용하여 읽기 전용), UpdateForm.js를 입수했습니다.

저는 PHP로 작업했었기 때문에 항상 한 가지 형태로 작업했습니다.

저는 리액트와 레독스를 사용하여 가게를 관리합니다.

CreateForm 컴포넌트에 있는 경우 이 소품을 서브 컴포넌트에 전달합니다.createForm={true}값을 채우지 않고 비활성화하지 않도록 합니다.ViewForm 컴포넌트에서 이 소품을 전달합니다.readonly="readonly".

그리고 텍스트 영역에는 값으로 채워져 있어 업데이트할 수 없는 문제가 있습니다.값이 포함된 응답 텍스트 영역은 읽기 전용이지만 업데이트해야 합니다.

이러한 폼의 다른 상태를 처리하는 컴포넌트가 1개밖에 없는 가장 좋은 구조는 무엇입니까?

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

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

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

또한 복합 리듀서에 리듀스 폼 리듀서를 추가합니다.

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과 유사한 라이브러리)만 사용할 예정입니다.

react-redux-form(스텝 바이 스텝)도 있는데, redux-form의 javascript(& boilplate) 중 일부를 마크업 선언과 교환하는 것 같습니다.좋아 보이는데 아직 안 써봤어요.

Readme에서 잘라낸 붙여넣기:

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

편집: 비교

react-redux 형식의 문서에서는 다음과 같이 redux 형식의 비교 결과를 제공합니다.

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이치노

대형 라이브러리를 사용하지 않고 완전히 제어된 양식 구성 요소를 만들고 싶은 사용자를 위한 또 다른 사항입니다.

RedxFormHelper - 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 상태에 특정 데이터를 추가하는 것입니다.이러한 데이터에는 폼의 각 필드에 대한 오류 플래그 집합과 현재 필드 값이 포함됩니다.

폼 상태는 기존 리듀서에 추가하거나 별도의 리듀서에서 정의할 수 있습니다.

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

액션 예:

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

두 번째이자 마지막 단계는 당사의 양식에 맞는 컨테이너 구성요소를 생성하여 Redux 상태 및 액션의 각 부분에 연결하는 것입니다.

또한 양식 필드의 검증을 지정하는 양식 모델을 정의해야 합니다.에는 예시를 해보겠습니다.ReduxFormHelper컴포넌트의 멤버로 간주하여 폼 모델과 폼 상태의 콜백 디스패치 업데이트를 전달합니다.

후의 음 the the the the render() 하는 onChange폼의 and andonSubmit" " "를 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)

데모

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

반응형