简体   繁体   English

ReactJS | 防止 state 数组在输入字段更改时更新

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM