简体   繁体   English

React state 钩子数组元素不更新 state

[英]React state hooks array element not updating state

I'm trying to reduce the code and/or optimize the use of React state hooks in a form with the rn-material-ui-textfield module.我正在尝试减少代码和/或优化 React state 挂钩在带有rn-material-ui-textfield模块的表单中的使用。 Normally, for a single text field, you could do something like this通常,对于单个文本字段,您可以这样做

import { OutlinedTextField } from 'rn-material-ui-textfield'
// ...and all the other imports

const example = () => {
    let [text, onChangeText] = React.useState('');
    let [error, set_error] = React.useState('');

    const verify = () => {
        if(!text) set_error('Enter a text');
        else console.log('Finished');
    }

    return(
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
            <OutlinedTextField
                onChangeText={onChangeText}
                value={text}
                onSubmitEditing={verify}
                error={error}
              />
        </View>
    );
}

and it would surely work no problem.它肯定会工作没问题。 But as you keep on adding more and more fields, setting a separate error and text hooks for each of them seem tedious and generates a lot of code.但是随着您不断添加越来越多的字段,为每个字段设置单独的errortext挂钩似乎很乏味并且会生成大量代码。 So, in order to prevent this, I tried to write this in a different way所以,为了防止这种情况,我试着用不同的方式写这个

// ...all imports from previous snippet

const example = () => {
    let [inputs, set_input_arr] = React.useState(['', '', '', '', '', '']);
    let [error, set_error_arr] = React.useState(['', '', '', '', '', '']);
    const error_names = ['your name', 'an email ID', 'a password', 'your phone number', "your favourite color", 'your nickname'];

    const set_input = (index, text) => {
        let temp = inputs;
        temp[index] = text;
        set_input_arr(temp);
    };

    const set_error = (index, err) => {
        let temp = error;
        temp[index] = err;
        set_error_arr(temp);
        // this logs the array correctly after running the loop each time
        console.log(`This was set as error: ${error}`);
    };

    const verify = () => {
        for (let i = 0; i < inputs.length; i++) {
            if (!inputs[i]) set_error(i, `Please enter ${error_names[i]}`);
        }
    };
 
    return(
        <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
            <OutlinedTextField
                onChangeText={text => set_input(0, text)}
                value={inputs[0]}
                error={error[0]}
             />
             <OutlinedTextField
                onChangeText={text => set_input(1, text)}
                value={inputs[1]}
                error={error[1]}
             />
             <OutlinedTextField
                onChangeText={text => set_input(2, text)}
                value={inputs[2]}
                error={error[2]}
             />
             <OutlinedTextField
                onChangeText={text => set_input(3, text)}
                value={inputs[3]}
                error={error[3]}
             />
             <OutlinedTextField
                onChangeText={text => set_input(4, text)}
                value={inputs[4]}
                error={error[4]}
             />
             <OutlinedTextField
                onChangeText={text => set_input(5, text)}
                value={inputs[5]}
                error={error[5]}
                onSubmitEditing={verify}
             />
             <Button onPress={verify} title='Verify' />
        </View>
    );
}

and it doesn't work.它不起作用。 To be clear, the console.log() in the set_error() does print the out as I expected.需要明确的是, set_error()中的console.log() ) 确实按照我的预期打印了输出。 It adds all the values to the array and prints out the complete array.它将所有值添加到数组并打印出完整的数组。 But then, the state in the elements doesn't change.但是,元素中的 state 并没有改变。 I strongly believe that this has got something to do with React's way of handling hooks rather than a bug in the <OutlinedTextField /> or something.我坚信这与 React 处理钩子的方式有关,而不是<OutlinedTextField />或其他东西中的错误。 That's why I'm leaving this here.这就是为什么我要把它留在这里。

If such an approach is impossible with React, please suggest another way to efficiently write code to declare and use these many textfields without declaring all these error hooks.如果这种方法对于 React 是不可能的,请建议另一种方法来有效地编写代码来声明和使用这么多文本字段,而无需声明所有这些错误挂钩。

To fix this change set_error_arr(temp);要修复此更改set_error_arr(temp); to set_error_arr([...temp]);set_error_arr([...temp]); . .

The reason React does not trigger a re-render when you write set_error_arr(temp);编写set_error_arr(temp); is because of how JS arrays work.是因为 JS arrays 是如何工作的。 temp is holding a reference to the array. temp持有对数组的引用。 This means, that even though the values may be changing the reference has not.这意味着,即使值可能发生变化,引用也没有发生变化。 Since, the reference has not changed React does not acknowledge a change has occurred to your array.因为,引用没有改变,React 不承认你的数组发生了变化。 By writing [...temp] you are creating a new array (new reference to point too) thus React acknowledges a change occurred and will trigger a re-render.通过编写[...temp]你正在创建一个新数组(对 point 的新引用)因此 React 承认发生了变化并将触发重新渲染。

This will also occur when working with JS objects使用 JS 对象时也会发生这种情况

It's because React doesn't think that the Array has changed because it is pointing to the same reference.这是因为 React 认为 Array 没有改变,因为它指向同一个引用。 The content of the array itself has changed, but React only checks if it is the same Array, not the content.数组本身的内容发生了变化,但 React 只检查是否是同一个数组,而不检查内容。

There are two different solution, the first one is to create a new Array with the same content like:有两种不同的解决方案,第一种是创建一个具有相同内容的新数组,例如:

 const set_error = (index, err) => {
        let temp = [...error];
        temp[index] = err;
        set_error_arr(temp);
        // this logs the array correctly after running the loop each time
        console.log(`This was set as error: ${error}`);
    };

Or you can checkout the useReducer hook which might be more aligned to what you're trying to implement或者您可以查看 useReducer 挂钩,它可能更符合您要实现的目标

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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