Getting started
Install dependencies (
redux,react,react-redux,redux-thunkare peer dependencies):yarn add redux react react-redux redux-thunk yarflDefine a config object with a
fieldsproperty describing what fields should be part of the Redux state and how they should be validated. Therulesstring describes which rules from validatorjs the field should be validated against (optional).//config.js export const myFormConfig = { name: 'myForm', fields: { name: { rules: 'required' }, email: { rules: 'required|email' }, age: { rules: 'required|min:18' } } }Create a reducer, initial state and a connector by passing the
configobject to theinitfunction and then simply create the Redux store as you normally would:// store.js import { init } from 'yarfl'; import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import { myFormConfig } from './config' // create a reducer, initial state and a connector const { reducer, initialState, connect } = init(myFormConfig); // redux-thunk is a peer dependency of yarfl const enhancers = applyMiddleware(thunk) const store = createStore(reducer, initialState, enhancers); export { store, connect }Set up the
Provideras you normally would with React Redux. To connect a React component with the store from the previous step use theconnectmethod that we created withinitin step three. Thisconnectmethod doesn't requiremapStateToPropsormapDispatchToPropsarguments, this is taken care of by Yarfl automatically. Just pass the React component as its first and only argument.// App.js import React from 'react'; import { Provider } from 'react-redux'; import { store } from './store.js' import MyFormComponent from './MyFormComponent.js'; class App extends React.Component { render() { return ( <Provider store={store}> <MyFormComponent /> </Provider> ) } }// MyFormComponent.js import React from 'react'; import { connect } from './store.js'; class MyFormComponent extends React.Component { ... } export default connect(MyComponent)When connected, a form prop of the name specified in the config object (in our case
myForm) is passed to the React component. ThemyFormprop is an object containing a handful of properties and methods to interact with the store.class MyComponent extends React.Component { render () { const { select, valid, errors } = this.props.myForm; /** * The 'select' function accepts a key string and * returns an object containing the name field * object from the store with a 'bind' function. */ const nameField = select('name'); return ( <form> <div> {/* The 'bind' function returns an object of properties that can be spread on an input element effectively binding that component to update and track the state. */} <label>{nameField.label}</label><br/> <input {...nameField.bind()} /> </div> {/* The 'valid' property describes if all fields registered in the Redux store pass their validation rules. */} <button type="submit" disabled={!valid} /> <div> {/* The 'errors' array contains the first error message (if any) for all registered fields. */} <label>All errors:</label> <ul> {errors.map(err => <li>{err}</li>)} </ul> </div> </form> ) } }The
bindmethod returns an object containing input attributes (with"value") from the store andonChange,onBlurandonFocushandlers to dispatch update actions.const bindProps = select('name').bind(); /* bindProps = { value: '', default: '', id: 'name', name: 'name', type: 'text', label: 'Name', className: '', placeholder: 'Name', disabled: false, autoFocus: false, onChange: (e: InputEvent, value?: any) => void, onBlur: () => void, onFocus: () => void, } */Using the spread syntax will attach all the props to the input element, but you are free to choose which properties you want to use or create new ones from
bind's return values:render() { const { select } = this.props; const { id, value, onChange } = select('email').bind(); return ( ... <input id={`${id}-is-the-id`} name="custom-email-name" value={value} onChange={(e, value) => { console.log('About to dispatch...') onChange(e); console.log('Dispatch complete!') }} /> ... ) }To initiate more than one form just add more config objects as input parameters from the
initfunction from step three.import { loginFormConfig, newUserConfig, ... } from './configs' // pass one or more config objects to init const { reducer, initialState, connect } = init(loginFormConfig, newUserConfig, ...);