简体   繁体   中英

React not triggering re-render with useState hook

I have a form component structured by categories > fields > then multiple validity rows per field. I keep track of the form value state (the validity rows per field ID) with useState(). There is an add button in a child component that triggers addValidityRowCallback to add another validity row.

I am trying to update the state and trigger a re-render but it doesn't seem to work.

import React, {useState} from 'react'
import CollapsibleFieldset from "../../fieldsets/CollapsibleFieldset";
import PersonInfoTypeFormRow from "./PersonInfoTypeFormRow";
import cloneAndUpdate from 'immutability-helper';


export default function PersonInfoForm({personInfoFormDataJson, webServiceAddress}) {

    let [targetPersonId, formCategories, defaultFormValuesIndexedById] = parsePersonInfoJsonString(personInfoFormDataJson);

    const [ formValuesById, setFormValues ] = useState(defaultFormValuesIndexedById);

    const addValidityRowCallback = (personInfoTypeId) => {
        let newEmptyRow = {id:12345, value:["NEW"], valueTitle:"","validityStartDate":"",createdDateTime:"",createdByPersonId:1212};
        let newFormValuesState = cloneAndUpdate(formValuesById, {});
        newFormValuesState[personInfoTypeId].push(newEmptyRow);
        setFormValues(newFormValuesState);
    };

    return <form className="personInfoForm pure-form">
            {formCategories.map((category) => {
                return <CollapsibleFieldset key={category.id || category.title} legend={category.title}>
                    {category.personInfoTypes.map((personInfoType) => {
                        return <PersonInfoTypeFormRow {...personInfoType} key={personInfoType.id} addValidityRowCallback={addValidityRowCallback} values={formValuesById[personInfoType.id]}/>
                    })}
                </CollapsibleFieldset>
            })
            {/* DEBUG........ */}
            {Object.keys(formValuesById).map(fieldId => {
                return formValuesById[parseInt(fieldId)].map(validityRow => {
                    return <h3 key={validityRow.id}>{validityRow.id}</h3>
                })
            })}
        }
    </form>;
}

I can see in the debugger that my addValidityRowCallback function is called correctly, clones the existing state, adds a new row at the correct point in the structure, then passes the new state back to setFormValues() but no re-render?

UPDATE : I've fixed this (although admittedly don't fully understand why my original didn't work), see my answer below

The issue is that you render formCategories but set formValuesById to the state. So instead if saving formValuesById on the state, you need to save formCategories .

Ok, I fixed this by updating the object through the cloneAndUpdate ( Hooks API Reference ) method instead of afterwards

assign[personInfoTypeId] = {$push:[newEmptyRow]};
let newPersonInfoTypeFieldsState = cloneAndUpdate(formValuesById,assign);
setFormValues(newFormValuesState);

I think the library isn't actually doing a deep copy but re-using parts of the structure that haven't changed so I suspect trying to manually update this afterwards was actually updating the original reference that react new about and thus react recognised no difference when setting state. Using the correct $push syntax documented in the library fixed it.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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