reactjs 使用Immer.js进行React和还原

x4shl7ld  于 2023-03-01  发布在  React
关注(0)|答案(1)|浏览(171)

我有一个问题要问Immer.js与React.js和Redux。我很熟悉React.js。据我所知,它是不"允许"更新属性本身

props.someValue= 'someValue'

因此,您需要向组件本身传递一个回调函数,如下所示

<SomeComponent
   key={"componentKey"}
   someValue={"someValue"}
   onSomeValueChange={this.handleSomeValueChange.bind(this)}
/>

SomeComponent中,你可以这样调用这个函数:

...
this.props.onSomeValueChange('someNewValue');
...

或者您可以使用Redux处理此问题:

import React, { useState, useEffect } from 'react';
import { connect } from 'react-redux';
import { updateSomeValue } from '../actions/index';

function SomeComponent(props) {

    function onTextChanged(event) {
        props.updateSomeValue(event.target.value);
    }

    return (
        <>
            <input type="text" value={someValue} onChange={onTextChanged} />
        </>
    )
}

function mapStateToProps({ someValue }) {
    return {
        someValue
    };
}

function mapDispatchToProps(dispatch) {
    return {
        updateSomeValue: someValue => dispatch(updateSomeValue(someValue)),
    };
}
 
const Form = connect(
    mapStateToProps,
    mapDispatchToProps
)(SomeComponent);
 
export default Form;

上面的代码是一个非常简单的例子,因为someValue只是一个string,没有复杂的对象,现在,如果它变得更复杂,并且你有带子对象的对象,我仍然在寻找更新子对象的最佳方法。
过去,我使用lodash创建对象的"克隆",并在更新原始属性之前修改"克隆"。

function onTextChanged(event) {
    let updatedJob = _.cloneDeep(props.currentJob);
    for(let a = 0; a < updatedJob.job_texts.length; a++) {
        if(updatedJob.job_texts[a].id === props.jobText.id) {
            updatedJob.job_texts[a].text = event.target.value;
        }
    }
    props.updateCurrentJob(updatedJob);
}

这个解决方案确实有效,但正如你所看到的,它可能不是处理这个问题的最佳方式。现在我今天读到,我的解决方案也不推荐。你需要创建每个子对象的"副本",就我所知。然后我偶然发现了redux页面关于immer.js,但我不太确定,如何使用这个:
情况如下:我有一个对象currentJob,它有几个属性。其中一个属性是一个名为job_texts的子对象(array)。一个job_text有一个属性text,我需要更新它。
我想,我可以处理这个,这样:

let updatedJob = props.currentJob;
props.updateCurrentJob(
    produce(updatedJob.job_texts, draftState => {
        if(draftState.id === props.jobText.id) {
            draftState.text = text;
        }
    })
);

...但当然这是行不通的,因为上面的代码我是用子对象数组更新currentJob的。我怎样才能用immer.js更新对象currentJob中的一个job_text呢?

8yparm6h

8yparm6h1#

最好的方法是使用官方Redux工具包,它已经在使用createReducercreateSlice创建的简化器中包含了immer
这也是两年来使用Redux的普遍推荐方式。
通常,还应在减径器(而不是零部件)中具有“子对象更新逻辑”。
例如,使用RTK时,“切片”看起来像

const objectSlice = createSlice({
  name: 'object',
  reducers: {
    updateSubObjectText(state, action) {
      const jobText = state.job_texts.find(text => text.id === action.payload.id)
      if (jobText) {
        jobText.text = action.payload.text
      }
    }
  }
})

export const { updateSubObjectText } = objectSlice.actions
export default objectSlice.reducer

这将创建一个reducer和一个action creator updateSubObjectText用于你的组件中,它们将被连接在一起,action类型是你不关心的实现细节。
在组件中,您现在只需执行以下操作

dispatch(updateSubObjectText({ id, text }))

更多信息请参见this quick into to Redux Toolkitthe full official Redux "Essentials" tutorial,其中介绍了Redux Toolkit作为编写真实的Redux逻辑的推荐方法。
writing Reducers with immer上还有一个完整的文档页面。

相关问题