我有一个父组件,它是一种表单,它通过redux通过以下方式连接到商店:
// FormComponent
export default connect(mapStateToProps, mapDispatchToProps)(FormComponent);
在该表单中,我有一个输入属性列表,这些属性是表单的子组件:
我需要通过在输入失去焦点时通过传递包含所有属性的JSON对象进行更新API调用来将任何字段中的更改保存到服务器。
API调用类似于:
updatePersonInfo({firstname: 'new name', lastname: 'new name', address: 'new address' });
我的想法是将PersonInfo
对象作为道具从Form组件传递到所有子组件。每个子组件将从PersonInfo
道具更新一个属性,并使用更新的PersonInfo
对象调度UPDATE_PERSONINFO操作。
但是要做到这一点,我需要将所有子组件都连接起来以存储:
// FirstNameComponent
export default connect(mapStateToProps, mapDispatchToProps(FirstNameComponent);
// LastNameComponent
export default connect(mapStateToProps, mapDispatchToProps)(LastNameComponent);
// AddressComponent
export default connect(mapStateToProps, mapDispatchToProps)(AddressComponent);
但是我认为出于性能原因,我们应该避免使用connect。 另外,我也不相信我需要4个连接器来调用一个动作。
处理这种情况的最佳选择是什么?
答案 0 :(得分:1)
您可以直接在每个组件中进行API调用,以仅更新相应的属性,因此可以避免使用Redux。
例如。 updatePersonInfo({ firstname: 'new name' })
。
您的API端点应为PATCH
方法,该方法仅更新有效负载中的字段(在上面的示例中,它仅应修改与firstname
对应的字段)。
答案 1 :(得分:1)
对于这个问题,我可以提供一个我之前学到的好技巧,在这种情况下,我们应该使用容器组件的状态来创建受控表单组件,并在每次表单更改时更新状态,这样您就可以始终访问您的来自容器组件状态的表单数据。 (例如:由于实现了受控组件,我们可以通过或 始终将保存在容器中的可用表单数据从容器组件状态发送到Redux,或者在诸如案例关注另一个字段)。
所以我们开始吧:
(1),您需要为每个输入使用name
属性和onChange
,例如:
<input
type='text'
name={name}
className='form-control'
value={value}
onChange={onChange}/>
(2) onChange
函数放置在您的容器组件中,并通过props向下传递到您的输入组件,如下所示:
updateCourseState(e) {
const field = e.target.name
const _formData = {...this.state.formData}
_formData[field] = e.target.value // (*)
return this.setState({ formData: _formData}) // <= then update the state on every change
}
在这里(*),通过使用预先计算的动态变量名称,我们为temp _formData对象中的每个输入字段创建一个属性,并向其分配要传递的事件的目标值,然后更新状态
(3),最后您现在可以将状态传递给redux,当我阅读您的问题时,您可以使用Refs()
来跟踪何时使用焦点在另一个字段上时,您可以检查this tutorial on YouTube,并在更改焦点时在此处将数据发送到Redux。
答案 2 :(得分:0)
我认为您可以使用store.subscribe(listener)
比较所有更改并调用API来保存数据。 https://redux.js.org/api-reference/store#subscribe
我将此用于类似功能,我将所有商店数据保存在浏览器的localstorage中。
这是我的用法
import { loadState, saveState, loadLanguage } from "./common/localStorage";
class Main extends React.Component {
..........
store.subscribe(() => {
saveState(store.getState()); // Some DOM api calls.
});
..........
}
我的localStorage.js文件
export const loadState = () => {
try {
const serializedState = localStorage.getItem('state');
if (serializedState === null) {
return undefined;
}
return JSON.parse(serializedState);
} catch (err) {
return undefined;
}
};
export const saveState = (state) => {
try {
const serializedState = JSON.stringify(state);
localStorage.setItem('state', serializedState);
} catch (err) {
// die
}
};
export const saveLanguage = (code) => {
try {
localStorage.setItem('languageCode', code);
} catch (err) {
// die
}
};
export const loadLanguage = () => {
try {
const languageCode = localStorage.getItem('languageCode');
if (languageCode === null) {
return "pt-br";
}
return languageCode;
} catch (err) {
return "pt-br";
}
};
答案 3 :(得分:0)
我真的不知道您的SubComponents是什么样子(即:FirstNameComponent ...)
首先,由于具有主要组件,因此您实际上并不需要将它们连接到商店。
class FormComponent extends Component {
changed(data, item) {
const updatedData = {...this.props.data, item: data};
this.props.updateData(updatedData);
}
render() {
return (
<div>
<FirstNameComponent onChange={this.props.update} name={this.props.data.firstname} />
<LastNameComponent onChange={this.props.update} name={this.props.data.lastname} />
<AddressComponent onChange={this.props.update} name={this.props.data.address} />
</div>
);
}
正如您在此处看到的,我们具有将要连接到redux的“主要”组件。 它将从存储中获取值,并将获得用于更新后端数据的调用方法。
“子组件”是“愚蠢的”,因为它们仅获取数据,并在输入模糊时从其prop调用方法。
答案 4 :(得分:0)
但是我认为出于性能原因,我们应该避免使用connect。另外,我不认为我需要4个连接器来调用一个动作。
实际上,使用很多connect
可以提高性能(在大多数情况下)。
由于connect
足够聪明,并且通过使用浅比较功能,为我们节省了许多重新渲染。
Proof: Should I Only Connect My Top Component Or Can I Connect Multiple Components In My Tree
请注意,与多余的重新渲染(或DOM操作)相比,connect/mapStateToProps/mapDispatchToProps
计算相对便宜。复杂的选择器可能会例外(reselect
就是这样)。
您的案子还不够复杂,根本没有太大区别。唯一的区别不是性能,而是connect
的样板代码,几乎没有几个不同的组件存储到相同的存储数据和相同的动作/重击中。
并且,针对您的方案的最佳选择是仅连接表单组件,并使输入字段尽可能简单和简洁。另外,例如,您可能需要添加表单提交逻辑或表单验证-使表单组件足够智能的另一点。
// #1
class FormComponent extends React.Component {
onChange = (e) => {
// assuming that form component receives `person` from `mapStateToProps`
// create new person value: previous one + updated value
const person = {
...this.props.person,
[e.target.name]: e.target.value,
}
// update action, assuming that `updatePerson` accepts person object
// and was injected by `mapDispatchToProps`
updatePerson(person)
}
render() {
const { person } = this.props
// there are native HTML elements in example, you can swap with your own components
// but, if your components will not paste name attribute value
// you might need to use something like this: `value => this.onChange('firstName', value)
return (
<form>
<input name="firstName" value={person.firstName} onChange={this.onChange} />
<input name="lastName" value={person.lastName} onChange={this.onChange} />
<input name="address" value={person.address} onChange={this.onChange} />
</form>
)
}
}
这是一个简单的解决方案。如果您绝对需要使用FirstName/LastName/AddressComponent
,那么它可能看起来像这样(仍然connect
只使用表单组件):
// #2
// form's onChange handler
onChange = (name, value) => {
const person = { ...this.props.person, [name]: value }
this.props.updatePerson(person)
}
// form's render
<FirstNameComponent value={person.firstName} onChange={this.onChange} />
// FirstNameComponent, similar to LastName/AddressComponents
const FirstNameComponent = ({ value, onChange }) =>
<input value={value} onChange={e => onChange('firstName', value)} />
IMO,第一个(#1 )简单明了。而且,具有更好的性能。
如果您在这些输入组件中具有非常复杂的逻辑(例如,掩码输入,去抖动状态更新等),则第二个选项(#2 )可能会确定。
答案 5 :(得分:0)
您真的需要redux(或redux形式)吗?
尝试Formik。我发现它非常灵活,可以使用模糊处理程序或验证(仅发送有效吗?)进行api调用。