javascript - 反应JS | 防止状态数组在输入字段更改时更新
问题描述
因此,我有一个带有添加按钮的输入字段,该按钮应将输入字段的值推送到一个数组(由 useState 控制)和一个容器下方,该容器呈现该数组的每个元素。这个容器有一个“头像”,我随机设置了背景颜色,所以每个都是不同的。
问题是,每次我在输入字段上键入时,似乎这个数组都在发生变化,容器被重新渲染,背景颜色也发生了变化
这是代码
const [inputs, onChangeInput, isFormValid] = useForm(defaultInputs)
const randomBackgroundColor = () => {
let arrayOfColours = ['#7042FB', '#FA656F', '#F2A766', '#E6C15C', '#272933']
return arrayOfColours[Math.floor(Math.random() * arrayOfColours.length)]
}
<div>
<p>Share</p>
<div>
<Input
type="email"
label="Add new member"
placeholder="member email"
value={inputs[InputName.USER].value}
onChange={(evt) => onChangeInput( InputName.USER, evt.target.value, evt.target.checkValidity() ) }
customIcon={<PeopleIcon width={16} height={16} />}
required
minLength={3}
/>
<button type="button" onClick={() => {
setArrayOfUsers((arrayOfUsers: any) => [...arrayOfUsers, inputs[InputName.USER].value,])
inputs[InputName.USER].value = ''
}}
> Add </button>
</div>
</div>
<div>
<div>Team</div>
<div>
<p>Members</p>
<div>
{arrayOfUsers.map((user: string) => (
<div>
<div>
<Avatar
name={user}
style={{
backgroundColor: randomBackgroundColor(),
color: '#ffffff', }} />
</div>
<span>{user}</span>
</div>
))}
</div>
</div>
</div>
export const useForm = (
defaultInputs: FormInputs
): [
FormInputs,
(key: string, value: string | boolean, validity: boolean) => void,
boolean
] => {
const [inputs, setInputs] = useState(defaultInputs)
const [isFormValid, setIsFormValid] = useState(false)
const onChangeInput = (
key: string,
value: string | boolean,
validity: boolean
) => {
const input = {
...inputs[key],
value,
isValid: validity,
dirty: true,
}
const newInputs = { ...inputs, [key]: input }
const isFormValid = checkForm(newInputs)
setInputs(newInputs)
setIsFormValid(isFormValid)
}
return [inputs, onChangeInput, isFormValid]
}
以及行为的 gif
我试图用它useRef
来解决这个问题,但问题是backgroundColor
每封电子邮件都是一样的,这不是预期的行为。
我也知道这段代码是不可重现的,但我正在处理的项目非常大,不可能gist
为它创建一个合适的
解决方案
好的,所以重点是您需要将颜色值存储在状态中。我通过创建一个新的状态变量来做到这一点,该变量以颜色数组开始,并为 api 调用返回的每个用户填充一种新颜色。然后,当使用“添加”按钮添加用户时,还会将新颜色推送到数组中。
在 JSX 中,我访问了映射 arrayOfUsers 的映射函数的索引,并使用该索引从颜色数组中获取颜色
我已将代码包装在一个示例组件中,因此它是一个完整的单元(除了您的问题中没有的变量声明,例如 defaultInputs 和 InputName)
const ExampleComponent = () => {
const [arrayOfUsers, setArrayOfUsers] = useState([]);
const [colors, setColors] = useState([]);
const [inputs, onChangeInput, isFormValid] = useForm(defaultInputs);
const generateRandomColor = () => {
let arrayOfColours = ['#7042FB', '#FA656F', '#F2A766', '#E6C15C', '#272933']
return arrayOfColours[Math.floor(Math.random() * arrayOfColours.length)];
}
const colorsArrayFactory = users => {
const colorsArr = [];
// handle invalid input
if (!users || !Array.isArray(users)) {
return colorsArr;
}
// good ol' fashioned for loop
for (let i = 0; i < users.length; i++) {
colorsArr.push(generateRandomColor());
}
return colorsArr;
}
// this function will add a new color to the array (called below in onButtonClick handler)
const addColorToArr = () => {
setColors([...colors, generateRandomColor()])
}
// assuming you will be fetching some users from the API when the component mounts
const fetchUsers = () => {
API_call()
.then(res => {
// assuming the array of users is a property of the repsonse object
const {users} = res;
setArrayOfUsers(users);
const colorsArr = colorsArrayFactory(users);
setColors(colorsArr);
})
}
// fetch users when component mounts
useEffect(() => {
fetchUsers()
}, [])
// I have just moved the click handler to a function so its easier to read
// I am not sure why you were passing a function to the setArrayOfUsers call - we have access to arrayOfUsers here anyway
// I also added a call to the function to add a color to the arr
const onButtonClick = () => {
setArrayOfUsers([...arrayOfUsers, inputs[InputName.USER].value,]);
addColorToArr();
inputs[InputName.USER].value = ''; // I question this line though - it looks like you are directly manipulating a state variable
}
return (
<div>
<div>
<p>Share</p>
<div>
<Input
type="email"
label="Add new member"
placeholder="member email"
value={inputs[InputName.USER].value}
onChange={(evt) => onChangeInput( InputName.USER, evt.target.value, evt.target.checkValidity() ) }
customIcon={<PeopleIcon width={16} height={16} />}
required
minLength={3}
/>
<button
type="button"
onClick={onButtonClick}>
Add
</button>
</div>
</div>
<div>
<div>Team</div>
<div>
<p>Members</p>
<div>
{arrayOfUsers.map((user: string, idx: number) => (
<div
key={idx}> /* arrays of elements should always have a key */
<div>
<Avatar
name={user}
style={{
backgroundColor: colors[idx], /*grab color from colors array here*/
color: '#ffffff', }} />
</div>
<span>{user}</span>
</div>)
)}
</div>
</div>
</div>
</div>
)
}
推荐阅读
- totp - TOTP:秒数吗?
- arrays - vba - 循环内的循环冻结excel
- php - 使用 Bootstrap Modal 进行 Laravel 验证
- hadoop - 使用 Flume 将本地文件源到 HDFS 接收器
- c# - 将多线程与 Async-await C# 结合使用
- python - protobuf,和tensorflow安装,选择哪个版本
- amazon-web-services - 为什么我在一个区域的 ec2 试图使用另一个区域的另一个 ec2 实例的私有 IP?
- mysql - 无法从子查询结果中选择计算值
- html - 在不离开父级的情况下覆盖 div
- c# - Elastic/Nest 中的滚动和分页 6+