[英]ReactJS | Prevent state array from updating on input field change
So I have an input field with an add button with a function that should push the value of input field to an array (controlled by useState ) and a container down below that renders each element of this array.因此,我有一个带有添加按钮的输入字段,该按钮带有 function 应该将输入字段的值推送到一个数组(由 useState 控制)和一个容器下方,该容器呈现该数组的每个元素。 This container has an 'avatar' to which I'm setting the background color randomly so each one is different.这个容器有一个“头像”,我随机设置了背景颜色,所以每个都是不同的。
The problem is, each time I type on the input field, it seems that this array is getting changed and the container is re-rendered and the background color changes问题是,每次我在输入字段上键入时,似乎这个数组都在发生变化,容器被重新渲染,背景颜色也发生了变化
Here's the code这是代码
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]
}
And a gif of the behaviour以及行为的 gif
I've tried to use useRef
to solve this but the problem is that then the backgroundColor
is the same for every email, which is not the intended behaviour.我尝试使用useRef
来解决这个问题,但问题是每个 email 的backgroundColor
颜色都是相同的,这不是预期的行为。
I'm also aware that this bit of code is not reproducible but the project I'm working on is quite large and impossible to create a proper gist
for it我也知道这段代码是不可重现的,但我正在处理的项目非常大,不可能为它创建一个合适的gist
Ok, so the main point is that you need to store the color values in state.好的,所以重点是您需要将颜色值存储在 state 中。 I have done this by creating a new state variable, which starts as an array of colors and is populated with a new color for each user returned by the api call.我通过创建一个新的 state 变量来完成此操作,该变量以 colors 的数组开始,并为 api 调用返回的每个用户填充一种新颜色。 Then, when a user is added using the 'Add' button, a new color is also pushed to the array.然后,当使用“添加”按钮添加用户时,还会将新颜色推送到数组中。
In the JSX I have accessed the index of the map function that maps arrayOfUsers and used that index to grab the color from the colors array在 JSX 中,我访问了映射 arrayOfUsers 的 map function 的索引,并使用该索引从 colors 数组中获取颜色
I have wrapped the code in an example component so it's a complete unit (except for the variable declarations that were not in your question such as defaultInputs and InputName)我已将代码包装在示例组件中,因此它是一个完整的单元(除了您的问题中没有的变量声明,例如 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>
)
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.