react派生状态的一点思考

这篇文章记录在做一个编辑弹框时,遇到表单加载初始数据以及维护用户修改数据问题的一个心路历程

在这里,先了解一下受控和非受控的概念:
  • 受控组件:组件内的数据是通过props传入的,受到父组件影响,自己无法控制数据
  • 非受控组件:组件内的数据是完全由自己维护了一份state,自己来管理自己的数据
思考的缘起

再次回到react上,接手一个新功能模块(特权价-企业内购)的开发,其中有一个功能是弹框编辑。刚刚学习了dva,于是也想把它用起来,信心满满开始编码

  • 1.新建一个编辑弹框的组件,接受一个idprops
  • 2.组件的componentDidMountdispatch一个根据id去查询详细信息的effect
  • 3.mapStateToProps把详细信息映射到props里,然后用就完了

以上数据完美渲染,好像没有一点问题,开开心心进行下一步,准备处理用户输入的数据了。
到这里,问题来了表单渲染的数据来自于props,但是props的数据是不能直接修改的,怎么办,我怎么保存用户输入的数据?

想到了两个方案

  • 方案一:
    监听表单项的onChange事件,将数据维护在state里,submit的时候把propsstate的数据合并提交
  • 带来的问题:
    1.如果表单项比较多,那submit时候的数据合并会很麻烦很麻烦。
    2.如果表单存在联动,例如修改了A需要联动B的内容,怎么办?因为渲染的数据来自props,所以就算在A的onChange中修改了B,也没法渲染到视图
  • 方案二:
    派生状态,在钩子函数componentWillReceiveProps中根据接收到的props重新去派生一份state,组件里数据渲染也好维护也好都针对state。
  • 带来的问题:
    当父组件重新渲染时,哪怕子组件接受的props没有变化,也会触发子组件的componentWillReceiveProps,设想意向用户开开心心正在输入,但是因为父组件的某个状态变化导致我们在子组件的componentWillReceiveProps中派生状态的代码触发,然后用户输入的信息全部被还原成初始props,然后崩溃。

当然在方案二中的componentWillReceiveProps可以通过对比新旧props对应值是否变更来解决说到的问题,但是如果是一个比较庞大复杂的数据是不是也会很头疼

问题总结

其实以上两个方案带来的问题都是因为子组件表单中的数据来源不统一
不统一就在于,表单需要渲染的初始数据来自于父组件的props传递,但是因为用户输入或联动又需要在本地维护一份数据。
因为一个编辑表单,天然就存在着两份数据:初始数据、用户输入。由此导致我们可能需要用到派生状态,这样的话如果稍不注意就会引起问题。
而且官方文档也明确说明派生状态会导致代码冗余,并使组件难以维护

解决办法
  • 1.让子组件成为一个受控组件,不管是初始渲染的数据,还是用户输入或者联动,都通过props来做
    //dva model
    export default {
      state: { formData: {} },
      effects: {
        *inputChange({val, key}, {put}) {
          put({type: 'SET_FORM_DATA', val, key});
        },
        reducers: {
          SET_FORM_DATA(state, {val}){
            const formData = {...state.formData,[key]: val}
            return {...state, formData}
        }
      }
    //Child
    handleInput(val,key){
      this.props.dispatch({
        type: 'inputChange',
        val,
        key
      });
    }
    render() {
      this.props.onInput(e.target.value, 'key')} 
        value={this.props.formData['key']}/>
    
  • 2.通过派生状态,让子组件做一个非受控组件,在constructor里边根据props派生出一份state。这里的查询数据详情,就不能放在子组件中做了。
    //Child
    class Child extends React.Component {
      constructor(props) {
        super(props);
        this.state = {
          data: props.formData 
        }
      }
    }
    
    但是注意,这里一定要注意来自官方的友善提醒
    react派生状态的一点思考_第1张图片
    派生状态一定要注意的
  • 3.抛开dva老老实实在componentDidMount中发起数据查询,然后将查询结果赋值到state
  • 4.加入配合使用的组件库是antd,那么还是受控组件,初始加载的数据渲染交给props,组件内无需维护表单项的state。联动或者提交时数据获取,通过antd form的setFieldsValuegetFieldsValueapi操作。

你可能感兴趣的:(react派生状态的一点思考)