Znalazłem pakiet Redux Form . Robi naprawdę dobrą robotę!
Możesz więc używać Redux z React-Redux .
Najpierw musisz utworzyć komponent formularza (oczywiście):
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',
fields: ['name', 'address', 'phone'],
validate: validateContact
})(ContactForm);
export default ContactForm;
Następnie łączysz komponent obsługujący formularz:
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', {}));
}
render() {
return (
<div id="app">
<h1>App</h1>
<ContactForm onSubmit={this.handleSubmit.bind(this)}/>
</div>
);
}
}
export default connect()(App);
I dodaj reduktor formy redux do swoich połączonych reduktorów:
import { combineReducers } from 'redux';
import { appReducer } from './app-reducers';
import { reducer as formReducer } from 'redux-form';
let reducers = combineReducers({
appReducer, form: formReducer
});
export default reducers;
A moduł walidatora wygląda tak:
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;
}
Po wypełnieniu formularza, gdy chcesz wypełnić wszystkie pola jakimiś wartościami, możesz skorzystać z initialize
funkcji:
componentWillMount() {
this.props.dispatch(initialize('contact', {
name: 'test'
}, ['name', 'address', 'phone']));
}
Innym sposobem wypełniania formularzy jest ustawienie wartości początkowych.
ContactForm = reduxForm({
form: 'contact',
fields: ['name', 'address', 'phone'],
validate: validateContact
}, state => ({
initialValues: {
name: state.user.name,
address: state.user.address,
phone: state.user.phone,
},
}))(ContactForm);
Jeśli masz inny sposób, aby sobie z tym poradzić, po prostu zostaw wiadomość! Dziękuję Ci.
redux-forms
? Zastanawiam się, jak ta płytka skaluje się w porównaniu z formami reakcjiredux-form
jest fatalna we wszystkich wersjach IE, w tym Edge. Jeśli musisz to wesprzeć, poszukaj gdzie indziej.AKTUALIZACJA: to rok 2018 i zawsze będę używał tylko Formika (lub bibliotek podobnych do Formika)
Istnieje również formularz reaktywuj-redux ( krok po kroku ), który wydaje się zamieniać część javascript (& boilerplate) formularza redux- a z deklaracją znaczników. Wygląda dobrze, ale jeszcze go nie używałem.
Wytnij i wklej z pliku 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);
Edycja: porównanie
Dokumentacja formularza reaktora redux zapewnia porównanie z formą reduxu:
https://davidkpiano.github.io/react-redux-form/docs/guides/compare-redux-form.html
źródło
Tym, którym nie zależy na ogromnej bibliotece do obsługi spraw związanych z formularzami, poleciłbym redux-form-utils .
Może generować wartości i zmieniać programy obsługi dla kontrolek formularzy, generować redukcje formularza, przydatne kreatory akcji do czyszczenia niektórych (lub wszystkich) pól itp.
Wszystko, co musisz zrobić, to złożyć je w swoim kodzie.
Używając
redux-form-utils
, skończysz z manipulacją formułą, taką jak: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> ); } }
Jednak to tylko biblioteka rozwiązuje problemu
C
iU
naR
iD
, być może bardziej zintegrowanyTable
komponent jest antipate.źródło
Kolejna rzecz dla tych, którzy chcą stworzyć w pełni kontrolowany komponent formy bez korzystania z ponadgabarytowej biblioteki.
ReduxFormHelper - mała klasa ES6, poniżej 100 linii:
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} } }
Nie wykonuje całej pracy za Ciebie. Ułatwia jednak tworzenie, walidację i obsługę kontrolowanego komponentu formularza. Możesz po prostu skopiować i wkleić powyższy kod do swojego projektu lub zamiast tego dołączyć odpowiednią bibliotekę -
redux-form-helper
(wtyczka!).Jak używać
Pierwszym krokiem jest dodanie określonych danych do stanu Redux, które będą reprezentować stan naszego formularza. Dane te będą zawierać bieżące wartości pól, a także zestaw flag błędów dla każdego pola w formularzu.
Stan formy można dodać do istniejącej redukcji lub zdefiniować w oddzielnej redukcji.
Ponadto konieczne jest zdefiniowanie konkretnej akcji inicjującej aktualizację stanu formularza oraz odpowiedniego kreatora akcji.
Przykład działania :
export const FORM_UPDATE = 'FORM_UPDATE' export const doFormUpdate = (data, errors) => { return { type: FORM_UPDATE, data, errors } } ...
Przykład reduktora :
... 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 || {}) } ... } }
Drugim i ostatnim krokiem jest utworzenie komponentu kontenera dla naszego formularza i połączenie go z odpowiednią częścią stanu i akcji Redux.
Musimy również zdefiniować model formularza określający walidację pól formularza. Teraz tworzymy instancję
ReduxFormHelper
obiektu jako element składowy komponentu i przekazujemy tam nasz model formularza oraz wywołanie zwrotne wysyłające aktualizację stanu formularza.Następnie w
render()
metodzie komponentu musimy powiązać zdarzenia każdego polaonChange
i formularza odpowiednioonSubmit
z metodamiprocessField()
iprocessForm()
, a także wyświetlić bloki błędów dla każdego pola w zależności od flag błędów formularza w stanie.Poniższy przykład wykorzystuje CSS z frameworka Twitter Bootstrap.
Przykład komponentu kontenera :
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)
Próbny
źródło