在 redux 状态下更改值

IT技术 reactjs redux
2021-05-22 00:21:01

语境

我正在渲染带有一组动态文本元素的表单。我已经使用normalizr 原则标准化了我的状态,因此有一个 elementIds 数组和一个包含 elementIds 引用的元素属性的对象(请参阅下面代码示例中的初始状态)。

目标

我的目标只是让两个渲染元素可编辑。我使用 onChange 回调成功地将操作 CHANGE_ELEMENT_VALUE 分派到我的商店,并且 action.id(引用更改元素的 id)和 action.value(新值)在 reducer 中可用(参见下面的代码)。

问题

我的问题是当我输入时文本字段没有改变,即使我可以看到使用 devtools redux 扩展的状态改变。我的理解是,react 无法识别状态更改,因为更改在状态中很深,而且我没有成功创建新的状态对象,我可能以某种方式引用了旧实例。

减速机代码

下面是我试图强制一个新的状态对象的拙劣尝试。我假设它不起作用,因为我的组件没有被重新渲染。也显得非常不雅观。

let initialState = {
    isLoading: false,
    data: {
        elementIds: ['name', 'email'],
        elements: {
            'name': {'id': 'name', 'value':'ben'},
            'email': {'id':'email', 'value':'ben@test.com'},
        },
    },
    error: false
}

function formReducer(state = initialState, action = null) {
    switch(action.type) {
        case types.CHANGE_ELEMENT_VALUE:
            let newData = Object.assign(state.data)
            newData.elements[action.id].value = action.value
            return {...state, data: newData}

        default:
            return state;
    }
}

export default formReducer;
2个回答

您可以immutability-helper npm package在减速器中使用和更新您的值

import update from 'immutability-helper';

let initialState = {
    isLoading: false,
    data: {
        elementIds: ['name', 'email'],
        elements: {
            'name': {'id': 'name', 'value':'ben'},
            'email': {'id':'email', 'value':'ben@test.com'},
        },
    },
    error: false
}

function formReducer(state = initialState, action = null) {
    switch(action.type) {
        case types.CHANGE_ELEMENT_VALUE:
            return update(state, {
                data : {
                    elements: {
                         [action.id]: {
                               value: {
                                    $set: 'new value'
                                }
                          }
                    }
                }
            })

        default:
            return state;
    }
}

export default formReducer;

update()围绕此模式提供简单的语法糖,使编写此代码更容易。虽然语法需要一点时间来适应(尽管它的灵感来自 MongoDB 的查询语言),但它没有冗余,它是静态可分析的,并且与可变版本相比,它的类型不多。

Object.assign仅操作一层深度;即它不会递归地克隆整个对象树。因此,您的顶级对象被克隆,但它不会触发重新渲染,因为您的减速器会改变克隆对象深处的值。

我建议查看deep-extend包并按如下方式更新您的状态:

import extend from 'deep-extend';

...

return extend(state, {
  elements: {
    [key]: value
  }
});