所以我有一个带有添加按钮的输入字段,该按钮的功能应该将输入字段的值推送到一个数组(由 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
答案 0 :(得分:0)
好的,所以重点是您需要将颜色值存储在状态中。我通过创建一个新的状态变量来完成此操作,该变量从一个颜色数组开始,并为 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>
)
}