我正在使用通过onChange函数更新的嵌套状态对象,如下所示:
const [someState, setSomeState] = useState({
customer: [
{
name: "Bob",
address: "1234 Main Street",
email: "bob@mail.com",
phone: [
{
mobile: "555-5555",
home: "555-5555"
}
]
}
]
});
const updateSomeStatePhone = e => {
e.persist();
setSomeState(prevState => {
prevState.customer[0].phone[0].mobile = e.target.value;
return {
...prevState
};
});
};
<p>Update Mobile Number<p>
<select
value={someState.customer[0].phone[0].mobile}
onChange={updateSomeStatePhone}
>
<option value="123-4567">"123-4567"</option>
</select>
这完成了把戏。但是,目前,如果我想通过带有下拉菜单/输入字段等的大形式来更新多个状态属性,则必须为这些字段硬编码6个不同的onChange处理程序。
相反,我宁愿只使用一个onChange处理程序,并从form字段传递要更改的state属性的状态,但是我无法弄清楚语法:
const updateSomeState = (e, prop) => {
e.persist();
setSomeState(prevState => {
prevState.prop = e.target.value;
return {
...prevState
};
});
};
<p>Update Mobile Number<p>
<select
value={someState.customer[0].phone[0].mobile}
onChange={updateSomeState(e, prop)}
>
<option value="123-4567">"123-4567"</option>
</select>
我尝试使用不同类型的语法将传入的“ prop”值链接到prevState:
prevState.prop = e.target.value;
prevState.(prop) = e.target.value;
${prevState} + '.' + ${prop} = e.target.value; // Dumb, I know
但是该函数永远无法识别我从该函数传入的“ prop”。我敢肯定必须有一个简单的方法来做到这一点。任何帮助将不胜感激。
答案 0 :(得分:1)
是否必须是单个useState
钩子?我建议使用useReducer
或使用多个useState
钩子对其进行简化。
useState
钩子import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
const [name, setName] = React.useState("");
const [address, setAddress] = React.useState("");
const [email, setEmail] = React.useState("");
const [mobile, setMobile] = React.useState("");
const [home, setHome] = React.useState("");
const getResult = () => ({
customer: [
{
name,
address,
email,
phone: [
{
mobile,
home
}
]
}
]
});
// Do whatever you need to do with this
console.log(getResult());
return (
<>
<input
value={name}
placeholder="name"
onChange={e => setName(e.target.value)}
/>
<br />
<input
value={address}
placeholder="address"
onChange={e => setAddress(e.target.value)}
/>
<br />
<input
value={email}
placeholder="email"
onChange={e => setEmail(e.target.value)}
/>
<br />
<input
value={mobile}
placeholder="mobile"
onChange={e => setMobile(e.target.value)}
/>
<br />
<input
value={home}
placeholder="home"
onChange={e => setHome(e.target.value)}
/>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
useReducer
(具有简化状态)import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
const reducer = (state, action) => {
const { type, value } = action;
switch (type) {
case "SET_NAME":
return { ...state, name: value };
case "SET_ADDRESS":
return { ...state, address: value };
case "SET_EMAIL":
return { ...state, email: value };
case "SET_MOBILE":
return { ...state, phone: [{ ...state.phone[0], mobile: value }] };
case "SET_HOME":
return { ...state, phone: [{ ...state.phone[0], home: value }] };
default:
throw Error(`Unexpected action: ${action.type}`);
}
};
const initialState = {
name: "",
address: "",
email: "",
phone: [
{
mobile: "",
home: ""
}
]
};
function App() {
const [state, dispatch] = React.useReducer(reducer, initialState);
// Do what you need with state
console.log(state);
return (
<>
<input
value={state.name}
placeholder="name"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_NAME", value })
}
/>
<br />
<input
value={state.address}
placeholder="address"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_ADDRESS", value })
}
/>
<br />
<input
value={state.email}
placeholder="email"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_EMAIL", value })
}
/>
<br />
<input
value={state.phone.mobile}
placeholder="mobile"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_MOBILE", value })
}
/>
<br />
<input
value={state.phone.home}
placeholder="home"
onChange={({ target: { value } }) =>
dispatch({ type: "SET_HOME", value })
}
/>
</>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
答案 1 :(得分:1)
useReducer
是执行此操作的更好选择。互联网上的示例。
为什么不应该使用useState
传递对象是因为它的行为不像setState
。底层对象引用是相同的。因此,反应永远不会触发状态更改。如果要对对象使用相同的useState
。您可能必须实现自己的版本以扩展该版本(下面的示例),或者可以直接使用useReducer挂钩来实现相同的目的。
下面是一个useState
的示例,您可以在每次更改时注意到状态更新。
const [form, setValues] = useState({
username: "",
password: ""
});
const updateField = e => {
setValues({
...form,
[e.target.name]: e.target.value
});
};
请注意其中的...form
。您可以在每个所需的更新中执行此操作,也可以使用自己的实用程序或我提到的useReducer
。
现在进入您的代码,还有其他问题。
您正在将phone
用作可以作为对象的数组。或者更好的是单独的属性也可以。无害。
如果将customers
作为数组,则必须遍历记录。不只是通过硬编码来更新索引。如果只有一个客户,最好不要保留数组,而只保留一个对象。假设它是一系列客户,并且您正在遍历整个客户,那么这就是更新mobile
的方法。
const updatedCustomers = state.customers.map(item => {
const { phone } = item;
return { ...item, phone: { mobile: e.target.value }};
// returns newCustomer object with updated mobile property
});
// Then go ahead and call `setSomeState ` from `useState`
setSomeState(...someState, { customer: updatedCustomers });// newState in your case is
相反,我希望只使用一个onChange处理程序,然后传入 来自状态属性的表单字段中的状态 变化,但我不知道语法
如果您还没有从第一个示例中弄清楚这一点。这是简短的步骤。
[e.target.name]
return { ...item, phone: { [e.target.name]: e.target.value }};
答案 2 :(得分:0)
使用lodash的_.set助手。
const updateSomeState = (e, prop) => {
e.persist();
setSomeState(prevState => {
let customers = [...prevState.customers] // make a copy of array
let customer = {...customers[0]} // make a copy of customer object
_.set(customer, prop, e.target.value)
customers[0] = customer;
return {
...prevState, customers
};
});
};
顺便说一句,在您现有的updateSomeStatePhone
中,您正在修改应该是不可变的prevState
对象。