Sam Fisher
Sam Fisher

Reputation: 848

State hooks does not update state parameters on redux store update

I have and update user page. There exists a simple form based on functional component. I use hooks in order to work with state and input params. Default params(which can be changed) I get from redux store(I fetch them in useEffect). After page load store changes, bot hooks does not update necessary properties in form and there are empty inputs. How can I supposed, that after store update hookk would update state, but no. How can I solve this problem?

component:

const EditUserPage: React.FunctionComponent<Props> = () => {
    const dispatch = useDispatch();
    const history = useHistory();
    const {profileId} = useParams<UrlParams>();
    
    useEffect(() => {
        dispatch(getRolesList());
        console.log("URL PARAM=", profileId)
        dispatch(getProfileById(parseInt(profileId)));
    }, []);
    
    
    const isEmailDuplicated: boolean = useSelector(getEmailDuplicated);
    const rolesList: Array<PlainObject> = useSelector(userRolesList);
    const contetType: string = useSelector(adminContentType);
    const userProfileData: PlainObject = useSelector(selectedUserProfile);

    const [startDate, setStartDate] = useState<Date>(new Date());
    const [firstName, setFirstName] = useState<string>(userProfileData.firstName);
    const [lastName, setLastName] = useState<string>(userProfileData.lastName ? userProfileData.lastName : "");
    const [role, setRole] = useState<string>(userProfileData.user && userProfileData.user.roles[0].name);
    const [gender, setGender] = useState<string>(userProfileData.gender ? userProfileData.gender : "");
    const [address, setAddress] = useState<string>(userProfileData.address ? userProfileData.address : "");
    const [phone, setPhone] = useState<string>(userProfileData.telephone ? userProfileData.telephone : "");
    const [office, setOffice] = useState<string>(userProfileData.office ? userProfileData.office : "");
    const [socialNumber, setSocialNumber] = useState<string>(userProfileData.socialNumber ? userProfileData.socialNumber : "");
    const [about, setAbout] = useState<string>(userProfileData.about ? userProfileData.about : "");
    const [login, setLogin] = useState<string>(userProfileData.email ? userProfileData.email : "");

    const roleDropdownOptions = rolesList && rolesList.map((role) => {
        console.log("?????",userProfileData)
        return {
            key: role.id,
            text: role.name.toLowerCase(),
            value: role.name,
        }
    })

    //validate email
    const mailRegex = new RegExp(/^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/g);
    const isMailValid = login && mailRegex.test(login);

    //validate phone number
    const phoneRegex = new RegExp(/^[+]*[(]{0,1}[0-9]{1,4}[)]{0,1}[-\s./0-9]*$/g)
    const isPhoneValid = phone && phoneRegex.test(phone);
    
    const handleSubmit = (event: { preventDefault: () => void; }) => {
        const dateFormat = require("dateformat");

        event.preventDefault();

        userProfileData["firstName"] = firstName;
        userProfileData["lastName"] = lastName;
        userProfileData["email"] = login;
        userProfileData["telephone"] = phone;
        userProfileData["about"] = about;
        userProfileData["office"] = office;
        userProfileData["address"] = address;
        userProfileData["socialNumber"] = socialNumber;
        userProfileData["dateOfBirth"] = dateFormat(startDate, "yyyy-mm-dd");
        userProfileData["gender"] = gender;

        const promise = new Promise(() => {
            return dispatch(updateProfile(userProfileData));
        })

        promise
            .then(() => history.push("/admin"))
            .then(() => {
                if (contetType === adminContentTypes.PERSONAL) {
                    dispatch(getPersonalProfiles());
                } else {
                    dispatch(getPatientProfiles());
                }
            })
    }

    return (
        <Fragment>
            <Header as="h1">
                EDIT USER
            </Header>
            <Divider/>
            <Form onSubmit={handleSubmit}>
                {
                    isEmailDuplicated &&
                    <Message
                        negative={true}
                        header="Account already exists"
                        content="An account already exists for this email address, please log in or confirm your email address is correct"
                    />
                }
                <Form.Group widths="equal">
                    <Form.Input
                        label="First name"
                        name="firstName"
                        value={firstName}
                        required={true}
                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => setFirstName(e.target.value)}
                    />
                    <Form.Input
                        label="Last name"
                        name="lastName"
                        value={lastName}
                        required={true}
                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => setLastName(e.target.value)}
                    />
                    <Form.Input
                        label="Email"
                        name="login"
                        required={true}
                        value={login}
                        error={!isMailValid && login ? {
                            content: "Please enter a valid email address",
                            pointing: "below",
                        } : false}
                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => setLogin(e.target.value)}
                    />
                </Form.Group>
                <Divider hidden/>
                <Form.Group>
                    <Form.Select
                        width={4}
                        placeholder="User role"
                        label="User role"
                        name="user_role"
                        selection
                        options={roleDropdownOptions}
                        value={role}
                        required={true}
                        onChange={(event: React.ChangeEvent<HTMLSelectElement>, data: PlainObject) => {
                            event.preventDefault();
                            setRole(data.value);
                        }}
                    />
                    <Form.Select
                        width={4}
                        value={gender}
                        label="Gender"
                        name="gender"
                        selection
                        options={options}
                        required={true}
                        onChange={(event: React.ChangeEvent<HTMLSelectElement>, data: PlainObject) => setGender(data.value)}
                    />
                    <Form.Input
                        width={4}
                        label="Office"
                        name="office"
                        value={office}
                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => setOffice(e.target.value)}
                    />
                    <div style={{"paddingLeft": "7px", "paddingRight": "7px"}}>
                        <label>
                            Date of birth
                        </label>
                        <br/>
                        <DatePicker
                            selected={startDate}
                            onChange={(date: Date) => {
                                setStartDate(date)
                            }}
                            peekNextMonth
                            showMonthDropdown
                            showYearDropdown
                            dropdownMode="select"
                        />
                    </div>
                </Form.Group>
                <Divider hidden/>
                <Form.Group>
                    <Form.Input
                        width={4}
                        label="Address"
                        name="address"
                        value={address}
                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => setAddress(e.target.value)}
                    />
                    <Form.Input
                        width={4}
                        label="Phone number"
                        name="telephone"
                        value={phone}
                        required={true}
                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => setPhone(e.target.value)}
                        error={!isPhoneValid && phone ? {
                            content: "Please enter a valid phone number",
                            pointing: "below",
                        } : false}
                    />
                    <Form.TextArea
                        label="About"
                        value={about}
                        width={8}
                        onChange={(e: React.ChangeEvent<HTMLTextAreaElement>, data: PlainObject) => setAbout(data.value)}
                    />

                </Form.Group>
                <Divider hidden/>
                <Form.Group widths="equal">
                    <Form.Input
                        label="Social number"
                        name="socialNumber"
                        value={socialNumber}
                        onChange={(e: React.ChangeEvent<HTMLInputElement>) => setSocialNumber(e.target.value)}
                    />
                </Form.Group>
                <Divider hidden/>
                <Button
                    floated="right"
                    size="medium"
                    onClick={() => history.push("/admin")}
                >
                    Back
                </Button>
                <Button
                    color="blue"
                    floated="right"
                    size="medium"
                    disabled={!isMailValid || !isPhoneValid}
                >
                    Save
                </Button>
            </Form>
        </Fragment>
    );
}

export default EditUserPage;

Upvotes: 0

Views: 121

Answers (1)

Mike Rivet
Mike Rivet

Reputation: 106

I think you'll want another useEffect that watches for changes on userProfileData.

Right now you're initializing all your hooks state variables with the userProfileData, but on the first pass that data is empty.

When the store updates the userProfileData value should change but this component isn't set up to do anything with it.

const [firstName, setFirstName] = useState<string>(userProfileData.firstName);

If you also had a hook below that calls the set callbacks it should update once the store does.

useEffect(() => {
setFirstName(userProfileData.firstName);
}, [userProfileData])'

This might cause issues if you run into a scenario where

  • User has entered information
  • Store updates

If that instance is a possibility you could add logic at the beginning of the useEffect to not update the field if it's already been updated.

Upvotes: 2

Related Questions