Reputation: 31
fields
array contains different field objects, on every field using map function there is a remove button but handleRemove
function is causing error "Rendered more hooks than during the previous render."
const [fields, setFields] = useState([
{ name: "abc", value: "" },
{ name: "def", value: "" }
]);
...
{
{fields.map((field, index) => {
const [now, setNow] = useState(field.value);
const [isFocused, setIsFocused] = useState(false);
const handleRemove = (index) => {
const newFields = fields;
newFields.splice(index, 1);
setFields(newFields);
};
return (
<View key={index}>
<TextInput
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onChangeText={handleChange}
value={now}
/>
<TouchableOpacity
onPress={() => {
handleRemove(index);
}}
>
<Ionicons name="close" style={[tw``]} size={22} color="#ddd" />
</TouchableOpacity>
</View>
);
})}
}
Upvotes: 2
Views: 777
Reputation: 203257
The code is breaking the Rules of Hooks: Only call hooks at the top level, don't call hooks inside loops, conditions, or nested functions.
Effectively you should factor out most of the fields.map
callback into a React component so the useState
hooks are rendered with the JSX and not in the Javascript callback in the loop.
Example:
const Field = ({ field, handleRemove }) => {
const [now, setNow] = useState(field.value);
const [isFocused, setIsFocused] = useState(false);
return (
<View>
<TextInput
style={styles.input}
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
onChangeText={setNow}
value={now}
/>
<TouchableOpacity onPress={handleRemove}>
<Ionicons name="close" style={[tw``]} size={22} color="#ddd" />
</TouchableOpacity>
</View>
);
};
...
{fields.map((field, index) => {
const handleRemove = () => {
setFields((fields) => fields.filter((_, i) => i !== index));
};
return (
<Field key={field.name} field={field} handleRemove={handleRemove} />
);
})}
Note also the handleRemove
logic was updated to avoid a state mutation. Array.prototype.splice
mutates the array in-place.
Not ok:
const handleRemove = (index) => {
const newFields = fields; // <-- newFields is reference to state
newFields.splice(index, 1); // <-- .splice mutates state
setFields(newFields); // <-- same reference set back into state
};
Ok:
const handleRemove = () => {
// .filter returns new array reference
setFields((fields) => fields.filter((_, i) => i !== index));
};
Upvotes: 0