Reputation: 175
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.
Upvotes: 1
Views: 1523
Reputation: 433
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
Upvotes: 5
Reputation: 11
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
Upvotes: 1