首页 > 解决方案 > 反应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为它创建一个合适的

标签: javascriptreactjs

解决方案


好的,所以重点是您需要将颜色值存储在状态中。我通过创建一个新的状态变量来做到这一点,该变量以颜色数组开始,并为 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>
    )
}

推荐阅读