redux-form(V7.4.2)笔记( 五)核心模块回顾

本系列文章学习和研究redux-form的主要参考资源,特别是代码部分,主要在于其官方网站提供的一切资源及工程源码剖析。

基础

在使用 redux-form 之前,需要用户具备以下基础:


  • HTML5+CSS3
  • ES6+高阶组件函数式编程思想+React
  • Redux及React-redux

redux-form核心模块

redux-form基于React-Redux状态管理理念,而form本身作为一种「特殊」的容器组件,要实现这种组件与数据中心(即Redux的store)的交互,关键在于把握 redux-form 的三个主要模块:

  • formReducer reducer : 表单的各种操作以 Redux action 的方式,通过此 reducer 来促成 Redux store 数据的变化。
  • reduxForm() API :此高阶组件用以整合 Redux action 绑定的用户交互与您的组件,并返回一个新的组件供以使用。
  • <Field/> API: 用此代替您原生态的HTML5 <input/> 组件,可以与redux-form的逻辑相连接。

补充解释如下。

(一)关于redux-form的reducer

有关代码如下(store.js):

import { reducer as reduxFormReducer } from 'redux-form';

const rootReducer = combineReducers({
   //other custom reducers
  form: reduxFormReducer, // mounted under "form"
});

combineReducers工具函数组件各个子reducer,最后形成一个大型reducer,称为rootReducer。然后,以此rootReducer为参数传递给createStore创建Redux的store对象。

Reducer的作用是:负责根据子组件发出的Action和原有State生成新的State,即:

Reducer的作用
oldState+Action=>newState

为了全面理解上面的代码,让我们再回顾一下Redux编程中Reducer拆分思想。Redux编程思想中建议把较大型的超过20行以上代码的reducer函数拆成了若干个小型的recducer函数,每一个负责生成对应的一部分state。而且,这种拆分与 React 应用的结构相吻合:一个 React 根组件由很多子组件构成。这就是说,子组件与子 Reducer 完全可以一一对应。

Redux 提供的combineReducers方法,正是用于 Reducer 的拆分。你只要定义各个子 Reducer 函数,然后用这个方法,即可将它们合并成一个大的 Reducer。

典型情况下,上述combineReducers方法用场是:
生成rootReducer的模块中。因为往往随后使用rootReducer作为参数并通过调用createStore方法生成整个系统唯一的store,所以,combineReducers方法主要store.js模块中。

关于combineReducers方法还有一个重要细节,请注意如下代码:

const rootReducer = combineReducers({
  chatLogReducer,
  statusMessageReducer,
  userNameReducer,
    form: reduxFormReducer
});

这种写法有一个前提: State 的属性名必须与各个子 Reducer 同名;否则,就要采用下面的写法

const rootReducer = combineReducers({
chatLog:chatLogReducer,
statusMessage:statusMessageReducer,
userName:userNameReducer,
form: reduxFormReducer
});
因此,该函数根据 State 的 不同key 去执行相应的子 Reducer,并将返回结果合并成一个大的 State 对象。注意:最后一个form键名是不能更改的,这是redux-form系统规定的

将来使用上面代码的结果的方法是:
state.chatLog、state.statusMessage......
当然,由于一个典型的reducer是要返回一个新的状态——一般使用对象方式表示;所以,最终你会见到代码中出现类似于下面的引用形式:
state.chatLog.obj1.p1

这一点在redux-form官方网站提供的实例InitializeFromStateForm中文件InitializeFromStateForm.js中即有如下使用方式:

InitializeFromStateForm = connect(
state => ({
initialValues: state.accountReducer.data,
}),
{ load: loadAccount },
)(InitializeFromStateForm);

上面代码行出现在示例表单定义模块的代码中,这里通过connect方法调用进一步包装表单组件,实现把store中state有关数据(state.accountReducer.data)映射到表单组件的属性上。

啰嗦上面这一些,就是为了强调一个简短的combineReducers调用意义重大,正是这一调用最终把store数据(代码中典型称为state)与组件props关联到一起。

(二)关于reduxForm()方法

API部分的描述是:reduxForm(config:Object)
这里,参数config是一个对象类型。此对象规定了系统内置的许多key,分别实现不同的功能。此高阶组件方法用以整合 Redux action绑定的用户交互与您的组件,并返回一个新的组件供以使用。

上面示例中的代码用法如下:

InitializeFromStateForm = reduxForm({
  form: 'initializeFromState', // a unique identifier for this form
})(InitializeFromStateForm);

InitializeFromStateForm = connect(
  state => ({
    initialValues: state.accountReducer.data, // pull initial values from account reducer
  }),
  { load: loadAccount }, // bind account loading action creator
)(InitializeFromStateForm);

上面reduxForm方法调用至少有两层含义:

(1)此方法进一步封装上面定义的表单组件,从而实现组成表单的UI组件的属性(props)(包括表单具体定义中的各个内置属性与接下来通过connect创建的少数定制属性,例如load)与store中的数据(即state,这个state中的对应形式可能是对象也可能是函数)关联到一起。

(2)此方法返回一个新的表单组件,提供给index.js中ReactDOM.render方法最终渲染网页中的表单使用,有关代码如下:

ReactDOM.render(
  <Provider store={store}>
    <div style={{ padding: 15 }}>
      <h2>Initialize From State</h2>
      <InitializeFromStateForm onSubmit={showResults} />
      <Values form="initializeFromState" />
    </div>
  </Provider>,
  rootEl
);

(三)关于<Field/>组件

所有需要与 store 数据连接的表单组件,都可以用 <Field/>。在正确使用它之前,需要清楚三条基本概念:


  • 必须包含 name 属性。可以是简单的字符串,如 userName、password,也可以是复杂的结构,如 contact.billing.address[2].phones[1].areaCode。
    * 必须包含 component 属性。可以是一个组件、无状态组件或者DOM所支持的默认的标签(input、textarea、select)。
  • 其他所有属性会通过prop传递到元素生成器中,如 className。

使用方法列举如下:

1.组件

可以是任何自定义的 class 组件(如下面的MyCustomInput组件),或者其他第三方库。

/```
/ MyCustomInput.js
import React, { Component } from 'react'

class MyCustomInput extends Component {
render() {
const { input: { value, onChange } } = this.props
return (
<div>
<span>The current value is {value}.</span>
<button type="button" onClick={() => onChange(value + 1)}>Inc</button>
<button type="button" onClick={() => onChange(value - 1)}>Dec</button>
</div>
)
}
}

然后这样使用:

import MyCustomInput from './MyCustomInput'

//...

<Field name="myField" component={MyCustomInput}/>


### 2.无状态组件

这是一个非常灵活的使用 <Field/> 的方法。你必须在你的 render() 方法外定义它,否则它每次渲染都会被重建,并且由于组件的 prop 会变,就会强制 <Field/> 进行渲染。如果你在 render() 内部定义无状态组件,不但会拖慢你的程序的运行,而且组件的input每次都会在组件重新渲染的时候失去焦点。

//在方法 render() 外定义
const renderField = (field) => (
    <div className="input-row">
      <input {...field.input} type="text"/>
      {field.meta.touched && field.meta.error &&
       <span className="error">{field.meta.error}</span>}
    </div>
  )

//在render()方法内定义
<Field name="myField" component={renderField}/>

### 3.最简单且最常用的形式: input, select, or textarea

比如创建一个文字输入框组件

<Field component="input" type="text"/>



# 参考
(1)https://github.com/tedyuen/react-redux-form-v6-example#field-value-lifecycle
(2)http://www.ruanyifeng.com/blog/2016/09/redux_tutorial_part_one_basic_usages.html
(3)https://redux-form.com/7.4.2/docs/gettingstarted.md/

猜你喜欢

转载自blog.51cto.com/zhuxianzhong/2149484