Universal Thinker
Universal Thinker

Reputation: 181

Child component props not updating after parent's state changes

I declared a parent component called EditLinksModal that uses the useState hook. It has a state property hasError, which is a JS object whose fields are passed to multiple URLField child components.

However, the child does not seem to be getting the updated props. In fact, it seems that re-rendering isn't happening even though I am calling setHasError since any console.log statements in the child don't print anything.

I have 3 console.log statements below. The 2 console.log statements in the parent (EditLinksModal) are printing the correct and up-to-date state properties. However, the console.log in the child does not even execute. What am I doing wrong ?

Is this because I am using useState in combination with a local state property that is a JS Object (ie. hasError is an object, but not a really deeply-nested one). Even so, I am puzzled why the hasError state property is properly updated, but the child-component doesn't have its error prop updated.

const URLField = ({label, name, placeholder, value, onHandleChange, error, errorText}) => {
    console.log(error) // problem: not even executing or printing anything
    return (
        <>
            <Header5>{label}</Header5>
            <CustomInput 
                name={name}
                placeholder={placeholder}
                variant="text"
                value={value}
                onChange={(evt) => onHandleChange(name, evt.target.value)} 
            />
            {error && <SystemComponent color={theme.colors.alertAction}>{errorText}</SystemComponent>}
        </>
    )
}

const EditLinksModal = ({visible, handleCloseModal}) => {


    const [hasError, setHasError] = useState({
        facebookUrl: false,
        linkedInUrl: false,
        githubUrl: false,
        websiteUrl: false
    });
    const validateUrl = (errorList, fieldName, fieldValue, allowedHostsList = false) => {
    // .........
    }

    const handleSave = (evt) => {
        evt.preventDefault();

        const errors = hasError;
        validateUrl(errors, 'facebookUrl', formValues['facebookUrl'], ['facebook.com']);
        validateUrl(errors, 'githubUrl', formValues['githubUrl'], ['github.com']);
        validateUrl(errors, 'linkedInUrl', formValues['linkedInUrl'], ['linkedin.com']);
        validateUrl(errors, 'websiteUrl', formValues['websiteUrl']);
        console.log(errors); // this prints correct, up-to-date values

        setHasError(errors);
    }

    console.log(hasError); // this prints correct, up-to-date values
    return (
        <SystemComponent>
            <URLField
                label="Facebook"
                name="facebookUrl"
                placeholder="Search" 
                value={formValues["facebookUrl"]}
                error={hasError.facebookUrl}
                errorText={"Please Enter Valid Facebook Profile Url."}
                onHandleChange={handleChange}
                />
        </SystemComponent>
    )

Upvotes: 1

Views: 521

Answers (1)

Christos Lytras
Christos Lytras

Reputation: 37327

Your problem is that the state object errors has the same reference, thus setHasError does not update the state. If you want to mutate that same object errors, then use spread operator to create a new object with the same properties:

const handleSave = (evt) => {
    evt.preventDefault();

    const errors = hasError;
    validateUrl(errors, 'facebookUrl', formValues['facebookUrl'], ['facebook.com']);
    validateUrl(errors, 'githubUrl', formValues['githubUrl'], ['github.com']);
    validateUrl(errors, 'linkedInUrl', formValues['linkedInUrl'], ['linkedin.com']);
    validateUrl(errors, 'websiteUrl', formValues['websiteUrl']);
    console.log(errors); // this prints correct, up-to-date values

    // Use spread operator to create a new object
    setHasError({ ...errors });
}

Upvotes: 1

Related Questions