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() {
      <input 
        onChange={() =>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 
        }
      }
    }
    
    但是注意,这里一定要注意来自官方的友善提醒
    派生状态一定要注意的
  • 3.抛开dva老老实实在componentDidMount中发起数据查询,然后将查询结果赋值到state
  • 4.加入配合使用的组件库是antd,那么还是受控组件,初始加载的数据渲染交给props,组件内无需维护表单项的state。联动或者提交时数据获取,通过antd form的setFieldsValuegetFieldsValueapi操作。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357