Redux-Form 从 v5 迁移到 v6 的完整指南

Redux-Form 从 v5 迁移到 v6 的完整指南

redux-form A Higher Order Component using react-redux to keep form state in a Redux store redux-form 项目地址: https://gitcode.com/gh_mirrors/re/redux-form

前言

Redux-Form 是一个用于管理 React 表单状态的流行库。在 v6 版本中,它经历了一次重大的架构重构,这带来了显著的性能提升,但也意味着从 v5 升级到 v6 需要一些必要的代码修改。本文将详细解析这些变化,并提供清晰的迁移路径。

架构变化:控制反转

v6 版本最核心的变化是采用了控制反转的设计模式:

  1. v5 架构问题

    • 只有最外层的表单组件连接到 Redux 状态
    • 每次按键都会导致整个表单重新渲染
    • 对于大型表单(几十或上百个字段)性能问题明显
  2. v6 改进方案

    • 每个字段(Field)都直接连接到 Redux 存储
    • 外层表单组件的连接方式优化,避免不必要的重渲染
    • 这种变化显著提升了大型表单的性能

由于这种根本性的架构改变,v5 到 v6 没有渐进式的升级路径,必须进行完整的迁移。

字段处理:从 fields 到 Field 组件

v5 的实现方式

在 v5 中,你需要提供一个字段名称数组,然后通过 fields 属性获取字段对象:

// v5 示例
class MyForm extends React.Component {
  render() {
    const { fields: { username, password } } = this.props;
    return (
      <form>
        <input type="text" {...username} />
        <input type="password" {...password} />
      </form>
    );
  }
}

export default reduxForm({
  form: 'myForm',
  fields: ['username', 'password']  // 字段名称数组
})(MyForm);

v6 的新方式

v6 引入了 Field 组件,每个字段独立管理:

// v6 示例
const renderInput = ({ input, meta, type }) => (
  <div>
    <input {...input} type={type} />
    {meta.touched && meta.error && <span>{meta.error}</span>}
  </div>
);

class MyForm extends React.Component {
  render() {
    return (
      <form>
        <Field
          name="username"
          component={renderInput}
          type="text"
        />
        <Field
          name="password"
          component={renderInput}
          type="password"
        />
      </form>
    );
  }
}

export default reduxForm({
  form: 'myForm'
  // 不再需要 fields 数组
})(MyForm);

主要变化点

  1. 不再需要 fields 数组声明
  2. 使用 Field 组件替代直接展开字段对象
  3. 字段验证错误信息现在通过 meta 属性访问
  4. 可以创建可复用的字段渲染组件

表单提交处理

表单提交的主要变化是验证错误的处理方式:

v5 的提交处理

<MyForm
  onSubmit={values =>
    ajax.send(values).catch(error => {
      if (error.validationErrors) {
        return Promise.reject(error.validationErrors);
      }
      // 其他错误处理
    })
  }
/>

v6 的改进

<MyForm
  onSubmit={values =>
    ajax.send(values).catch(error => {
      if (error.validationErrors) {
        throw new SubmissionError(error.validationErrors);
      }
      // 其他错误处理
    })
  }
/>

关键变化是验证错误现在需要包装在 SubmissionError 对象中,这样可以明确区分验证错误和其他类型的错误。

状态映射与 Action 绑定

在 v5 中,reduxForm() 装饰器可以接受 mapStateToPropsmapDispatchToProps 参数。在 v6 中,这一功能被移除了,你需要手动使用 connect()

// v6 方式
import { connect } from 'react-redux';
import { reduxForm } from 'redux-form';

class MyForm extends React.Component {
  // ...
}

const mapStateToProps = (state) => ({
  // 映射状态
});

const mapDispatchToProps = {
  // 绑定 action creators
};

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(reduxForm({
  form: 'myForm'
})(MyForm));

验证机制

同步验证

同步验证在 v6 中保持不变,唯一需要注意的是如果使用 ImmutableJS,传入的 values 会是 Immutable.Map 类型,但错误信息仍需返回普通 JS 对象。

异步验证

异步验证的工作方式与 v5 完全相同,无需修改。

字段数组处理

对于字段数组(动态表单字段),v6 引入了 FieldArray 组件:

v5 的实现

render() {
  const { fields: { awards } } = this.props;
  return (
    <div>
      <ul>
        {awards.map((award, index) => (
          <li key={index}>
            <input type="text" {...award.input}/>
          </li>
        ))}
      </ul>
      <button onClick={() => awards.addField()}>Add Award</button>
    </div>
  );
}

v6 的新方式

const renderAwards = ({ fields }) => (
  <div>
    <ul>
      {fields.map((name, index) => (
        <li key={index}>
          <Field name={name} type="text" component="input"/>
        </li>
      ))}
    </ul>
    <button onClick={() => fields.push()}>Add Award</button>
  </div>
);

render() {
  return (
    <div>
      <FieldArray name="awards" component={renderAwards}/>
    </div>
  );
}

数据标准化(Normalization)

数据标准化的位置从 reducer 转移到了字段级别:

v5 的方式

const upper = value => value && value.toUpperCase();

const reducer = combineReducers({
  form: form.normalize({
    myForm: {
      myField: upper
    }
  })
});

v6 的方式

const upper = value => value && value.toUpperCase();

// 在组件中
<Field name="myField" component="input" normalize={upper}/>

状态结构变化

如果你使用了 plugin() API 监听其他 action,需要注意内部状态结构的变化:

v5 的状态结构

{
  myField: {
    value: 'myValue',
    initial: 'myInitialValue',
    asyncError: 'myAsyncError',
    submitError: 'mySubmitError',
    touched: true,
    visited: true
  }
}

v6 的新结构

{
  values: { myField: 'myValue' },
  initial: { myField: 'myInitialValue' },
  asyncErrors: { myField: 'myAsyncError' },
  submitErrors: { myField: 'mySubmitError' },
  fields: {
    myField: {
      touched: true,
      visited: true
    }
  }
}

常见问题

react-hot-loader 兼容性

如果你使用 react-hot-loader 1.X 并遇到 Uncaught TypeError: Cannot read property 'wrapped' of undefined 错误,需要升级到 3.X 版本。

总结

Redux-Form v6 通过控制反转和细粒度的 Redux 连接带来了显著的性能提升,特别是对于大型表单。虽然迁移需要一些工作,但新的 API 设计更加灵活和直观。主要迁移步骤包括:

  1. fields 数组转换为 Field 组件
  2. 更新表单提交错误处理
  3. 单独处理 Redux 连接
  4. 将字段数组转换为 FieldArray 组件
  5. 将标准化逻辑移到字段级别

通过遵循这些指导原则,你应该能够顺利地将应用从 v5 迁移到 v6,并享受新版本带来的性能优势。

redux-form A Higher Order Component using react-redux to keep form state in a Redux store redux-form 项目地址: https://gitcode.com/gh_mirrors/re/redux-form

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

阮曦薇Joe

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值