一、前言与说明
本系列短文是我学习与使用Facebook倡领下的React技术栈的一道小小的痕迹记录。需要说明的是,学习与使用redux-form库的前提是:(1)具有使用React(https://reactjs.org/)开发的基本经验;(2)使用Redux(https://redux.js.org/)开发的基本经验。
redux-form库的URL:https://redux-form.com/7.4.2/
说实在的,在学习React技术栈过程中,看到每一个其组成子库的logo标记我每每深感热血沸腾又压力满满——Facebook作为业界大牛,其引领下的React技术栈正风糜全球;React框架的科学设计不由得你不对时下流行的各种跨平台与前后端一体化方案作一番深度对比、思索与尝试。好吧,先上一下redux-form库的HOME页logo截图:
本文围绕redux-form库的第一个最基本的实例redux-form-simple-example(https://redux-form.com/7.4.2/examples/simple/)展开。
二、使用redux-form库的前提
使用react-redux库的一个基本思想是把组成网页界面的所有组件分解为两种类型:普通组件(也称为描述组件)与容器组件。那么,redux-form,作为form——组件的一种,应当划分为容器组件。当然,根据react-redux库官方建议,复杂的容器组件还应当根据实际情况作可能的进一步拆分(再分解为更小粒度的组件与容器组件)。
但从全局观察,要想能够使用redux-form(或者从另外一种角度说,顺利实现把redux-form这种表单容器组件关联到Redux Store),需要重点掌握下面三个主要内容(或者说子模块):
- formReducer reducer : 表单的各种操作将以 Redux Action 的方式,通过此Reducer 来最终促成Redux store 数据的变化。
- reduxForm()高阶组件: 此高阶组件用以整合 Redux Action 绑定的用户交互与您的组件,并返回一个新的组件供以使用。
- <Field/> : 用来代替原生的HTML5 <input/> 组件,可以与redux-form的逻辑相连接。
三、redux-form数据流示意
使用redux-form的一个好处是,在大部分情况下我们不需要关心如何创建Action,一切都是由库自动完成的。下图展示了redux-form一个简易但典型的数据流情况:
对上图作随心所欲的深度解析尚有一定难度。所以,我先翻译一下官方网站上对于此图的基本解释,如下:
举个简单的例子,我们有一个被 reduxForm() 创建的表单组件,里面有一个用 <Field/> 创建的 <input/> 组件,数据流大概是这个样子的:
- 用户点击这个 <input/> 组件,
- “Focus action” 被触发,
- formReducer 更新了对应的状态,
- 这个状态被传回 <input/> 组件中。
遵循与此类似的逻辑还有,诸如在这个 <input/> 中输入文字、更改其状态和提交表单,等等。
redux-form 还能基于上述流程处理许多事情,诸如:表单验证与格式化,多类型属性与Action的创建等。有关这些内容的讨论,我们将随着本系列的展开而进行下去。
四、redux-form总体使用流程
下面给出的redux-form总体使用流程代码来自于redux-form官方提供的第一个最基本的实例redux-form-simple-example。
步骤 1/3: 创建Form reducer
store需要知道如何处理来自于form组件的action。因此,需要在store中注册 formReducer(或者说把formReducer以参数方式传递给创建Store的函数createStore),这个formReducer服务于整个app中定义的所有表单组件,因此只需要注册一次。
下面代码见于store.js中(稍加英文注释):
import { createStore,combineReducers } from 'redux'; import { reducer as reduxFormReducer } from 'redux-form'; //The combineReducers helper function turns an object // whose values are different reducing functions into a // single reducing function you can pass to createStore //e.g. rootReducer = combineReducers({potato: potatoReducer,tomato: tomatoReducer}) const reducer = combineReducers({ form: reduxFormReducer,// mounted under "form" }); const store = (window.devToolsExtension ? window.devToolsExtension()(createStore) : createStore)(reducer); export default store;
值得特别注意的是,在reducer中合并的formReducer的key必须命名为”form”!
步骤 2/3: 编写表单组件(包括使用<Field/>组件)
为了使表单组件可以与store进行交互,需要使用高阶函数 reduxForm() 来包裹表单组件。正如下面的代码所展示的,它以props的方式提供了表单内的state以及执行提交表单操作的函数。
下面代码来自于SimpleForm.js。当然,官方网站处提供了更简单的实例代码(https://redux-form.com/7.4.2/docs/gettingstarted.md/)。
import React from 'react'; import { Field,reduxForm } from 'redux-form'; const SimpleForm = props => { const { handleSubmit,pristine,reset,submitting } = props; return ( <form onSubmit={handleSubmit}> <div> <label>First Name</label> <div> <Field name="firstName" component="input" type="text" placeholder="First Name" /> </div> </div> <div> <label>Last Name</label> <div> <Field name="lastName" component="input" type="text" placeholder="Last Name" /> </div> </div> <div> <label>Email</label> <div> <Field name="email" component="input" type="email" placeholder="Email" /> </div> </div> <div> <label>Sex</label> <div> <label> <Field name="sex" component="input" type="radio" value="male" /> {' '} Male </label> <label> <Field name="sex" component="input" type="radio" value="female" /> {' '} Female </label> </div> </div> <div> <label>Favorite Color</label> <div> <Field name="favoriteColor" component="select"> <option /> <option value="ff0000">Red</option> <option value="00ff00">Green</option> <option value="0000ff">Blue</option> </Field> </div> </div> <div> <label htmlFor="employed">Employed</label> <div> <Field name="employed" id="employed" component="input" type="checkBox" /> </div> </div> <div> <label>Notes</label> <div> <Field name="notes" component="textarea" /> </div> </div> <div> <button type="submit" disabled={pristine || submitting}>Submit</button> <button type="button" disabled={pristine || submitting} onClick={reset}> Clear Values </button> </div> </form> ); }; // export default reduxForm({ // form: 'simple',// a unique identifier for this form // })(SimpleForm); //或者使用如下表达方式: const SimpleForm = reduxForm({ // a unique name for the form form: 'simple' })(SimpleForm) export default SimpleForm
【注意】<Field/> 组件可以连接所有input类型组件的数据到store中,基本用法如下:
<Field name="inputName" component="input" type="text" />
它创建了一个text类型的<input/>组件,还提供了诸如 value、onChange、onBlur等属性,用于跟踪和维护此组件的各种状态。
还要注意的是, <Field/> 组件很强大,除了基本的类型,还可以配置类或者无状态组件,详见后文内容。
从现在开始,表单上的操作数据已经可以填充至Store,并可以执行提交表单操作了。
步骤 3/3: 提交表单数据
提交的数据以JSON对象的形式注入了此表单组件的 onSubmit 方法里了,请参考如下代码(index.js):
import React from "react"; import ReactDOM from "react-dom"; import { Provider } from "react-redux"; import { Values } from "redux-form-website-template"; import store from "./store"; import showResults from "./showResults"; import SimpleForm from "./SimpleForm"; const rootEl = document.getElementById("root"); ReactDOM.render( <Provider store={store}> <div style={{ padding: 15 }}> <h2>最简单类型表单示例</h2> <SimpleForm onSubmit={showResults} /> <hr/> <Values form="simple" /> </div> </Provider>,rootEl );
注意,为了简单起见,官方提供的上述示例中使用ES6异步技术实现了提交服务的模拟,见下面的代码(showResults.js):
const sleep = ms => new Promise(resolve => setTimeout(resolve,ms)); export default (async function showResults(values) { await sleep(500); // simulate server latency window.alert(`You submitted:\n\n${JSON.stringify(values,null,2)}`); });
五、小结
本文结合redux-form官方提供的第一个实例redux-form-simple-example分析了使用redux-form的主要技术与总体思路。在后续短文中,我将主要结合redux-form官方提供的由简单到复杂的示例展示讨论。说实在的,阮一峰老师在他的一篇文章中提醒的,React技术栈的选择与学习绝不是一件容易的事情——此过程中,我深深感觉到这一点。因此,如果短文中有不当的分析,十分希望有致力到这项内容的朋友批评纠正!
原文链接:https://www.f2er.com/react/301398.html