反应 - 使用 Immer.js 的 Redux
React - Redux with Immer.js
我有一个问题要 immer.js
reactjs
和 redux
。
我熟悉 reactjs
。
据我所知,“不允许”通过
更新 属性 本身
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);
}
此解决方案确实有效,但如您所见,这可能不是处理此问题的最佳方法。
现在我今天读到,也不推荐我的解决方案。据我所知,您需要为每个子对象创建一个“副本”。
然后我偶然发现了关于 immer.js
的 redux
页面,但我不太确定如何使用它:
情况如下:
我有一个对象 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
?
非常感谢!
最好的方法是使用官方的 Redux 工具包,它已经在使用 createReducer
和 createSlice
.
创建的 reducer 中包含了 immer
这也是两年来普遍推荐的Redux使用方式
通常,您还应该在 reducer 中拥有“子对象更新逻辑”,而不是在您的组件中。
因此对于 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
以在您的组件中使用。它们将被连接在一起,动作类型是您不关心的实现细节。
在您的组件中,您现在只需要做
dispatch(updateSubObjectText({ id, text }))
有关更多信息,请参阅 this quick into to Redux Toolkit or the full official Redux "Essentials" tutorial,其中介绍了 Redux Toolkit 作为编写真正的 Redux 逻辑的推荐方式。
writing Reducers with immer 上还有一个完整的文档页面。
我有一个问题要 immer.js
reactjs
和 redux
。
我熟悉 reactjs
。
据我所知,“不允许”通过
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);
}
此解决方案确实有效,但如您所见,这可能不是处理此问题的最佳方法。
现在我今天读到,也不推荐我的解决方案。据我所知,您需要为每个子对象创建一个“副本”。
然后我偶然发现了关于 immer.js
的 redux
页面,但我不太确定如何使用它:
情况如下:
我有一个对象 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
?
非常感谢!
最好的方法是使用官方的 Redux 工具包,它已经在使用 createReducer
和 createSlice
.
immer
这也是两年来普遍推荐的Redux使用方式
通常,您还应该在 reducer 中拥有“子对象更新逻辑”,而不是在您的组件中。
因此对于 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
以在您的组件中使用。它们将被连接在一起,动作类型是您不关心的实现细节。
在您的组件中,您现在只需要做
dispatch(updateSubObjectText({ id, text }))
有关更多信息,请参阅 this quick into to Redux Toolkit or the full official Redux "Essentials" tutorial,其中介绍了 Redux Toolkit 作为编写真正的 Redux 逻辑的推荐方式。
writing Reducers with immer 上还有一个完整的文档页面。