1

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. 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. 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. It adds all the values to the array and prints out the complete array. But then, the state in the elements doesn't change. 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. 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.

2 Answers 2

5

To fix this change set_error_arr(temp); to set_error_arr([...temp]);.

The reason React does not trigger a re-render when you write set_error_arr(temp); is because of how JS arrays work. temp is holding a reference to the array. 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. 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.

This will also occur when working with JS objects

Sign up to request clarification or add additional context in comments.

Comments

1

It's because React doesn't think that the Array has changed because it is pointing to the same reference. The content of the array itself has changed, but React only checks if it is the same Array, not the content.

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

1 Comment

Nope that doesn't work. Here, the console.log() says This was set as error: ,,,,,. I'll check into the useReducer hook though thanks for that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.