Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

React useReducer hook does not let me change input values of user information form

First time asking. Thanks in advance, hope I explain my problem properly.

A few weeks ago I started working with MERN stack and my problem appeared while trying to build a form where users could see/update their personal info.

To achieve this, I implemented a useEffect hook that gets the current user´s personal info through an axios get request and calls the dispatch function of a reducer with the returned data.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

I can see the form inputs auto-filled with the info correctly, but I can not change any of the input values manually like any regular user would do. Here is my code:

import React, { useContext, useEffect, useReducer } from 'react';
import { Helmet } from 'react-helmet-async';
import Form from 'react-bootstrap/Form';
import { Resalia } from '../Resalia';
import Button from 'react-bootstrap/Button';
import { toast } from 'react-toastify';
import { useNavigate } from 'react-router-dom';
import axios from 'axios';
import { getError } from '../utils';
import LoadingBox from '../components/LoadingBox';
import MessageBox from '../components/MessageBox';

const reducer = (state, action) => {
  switch (action.type) {
    case 'FIELD':
      return { ...state, [action.fieldName]: action.payload };
    case 'FETCH_REQUEST':
      return { ...state, loading: true };
    case 'FETCH_SUCCESS':
      return { ...state, user: action.payload, loading: false };
    case 'FETCH_FAIL':
      return { ...state, error: action.payload, loading: false };
    default:
      return state;
  }
};

export default function AdminPersonalInfoScreen() {
  const navigate = useNavigate();
  const { state } = useContext(Resalia);
  const { userInfo } = state;
  const { email } = userInfo;

  const [{ user, error, loading }, dispatch] = useReducer(reducer, {
    loading: true,
    user: {},
    error: '',
  });

  const {
    name,
    surname,
    phone1,
    phone2,
    address1,
    address2,
    city,
    province,
    postcode,
  } = user;

  useEffect(() => {
    if (!userInfo) {
      navigate('/signin?redirect=/admin/personal-info');
    }
  }, [userInfo, navigate]);

  useEffect(() => {
    const fetchData = async () => {
      dispatch({ type: 'FETCH_REQUEST' });
      try {
        const result = await axios.get(
          `/api/users/personal-info/${userInfo._id}`
        );
        dispatch({ type: 'FETCH_SUCCESS', payload: result.data });
      } catch (err) {
        dispatch({ type: 'FETCH_FAIL', payload: err.message });
      }
    };
    fetchData();
  }, [userInfo._id]);

  const submitHandler = async (e) => {
    e.preventDefault();

    try {
      const { data } = await axios.put(
        '/api/users/personal-info',
        {
          email,
          name,
          surname,
          phone1,
          phone2,
          address1,
          address2,
          city,
          province,
          postcode,
        },
        {
          headers: { Authorization: `Bearer ${userInfo.token}` },
        }
      );
      localStorage.setItem('userInfo', JSON.stringify(data));
      toast.success('Datos guardados con éxito');
    } catch (err) {
      toast.error(getError(err));
    }

  };

  return (
    <div>
      <Helmet>
        <title>Mis Datos Personales</title>
      </Helmet>
      <h2 className="mt-4 mb-5">Mis Datos Personales</h2>
      {loading ? (
        <LoadingBox />
      ) : error ? (
        <MessageBox variant="danger">{error}</MessageBox>
      ) : (
        <Form onSubmit={submitHandler}>
          <Form.Group className="mb-4" controlId="name">
            <Form.Label>Nombre</Form.Label>
            <Form.Control
              type="text"
              required
              value={user.name}
              onChange={(e) => {
                dispatch({
                  type: 'FIELD',
                  fieldName: 'name',
                  payload: e.currentTarget.value,
                });
              }}
            />
          </Form.Group>
          <Form.Group className="mb-4" controlId="surname">
            <Form.Label>Apellidos</Form.Label>
            <Form.Control
              type="text"
              value={surname}
              onChange={(e) => {
                dispatch({
                  type: 'FIELD',
                  fieldName: 'surname',
                  payload: e.currentTarget.value,
                });
              }}
            />
          </Form.Group>
          <Form.Group className="mb-4" controlId="email">
            <Form.Label>Email</Form.Label>
            <Form.Control type="email" value={email} required disabled />
          </Form.Group>
          <Form.Group className="mb-4" controlId="phone1">
            <Form.Label>Teléfono de Contacto</Form.Label>
            <Form.Control
              type="phone"
              value={phone1}
              required
              onChange={(e) => {
                dispatch({
                  type: 'FIELD',
                  fieldName: 'phone1',
                  payload: e.currentTarget.value,
                });
              }}
            />
          </Form.Group>
          <Form.Group className="mb-4" controlId="phone2">
            <Form.Label>Teléfono Adicional</Form.Label>
            <Form.Control
              type="phone"
              value={phone2}
              onChange={(e) => {
                dispatch({
                  type: 'FIELD',
                  fieldName: 'phone2',
                  payload: e.currentTarget.value,
                });
              }}
            />
          </Form.Group>
          <Form.Group className="mb-4" controlId="address1">
            <Form.Label>Dirección (nombre de la vía y número)</Form.Label>
            <Form.Control
              type="text"
              value={address1}
              onChange={(e) => {
                dispatch({
                  type: 'FIELD',
                  fieldName: 'address1',
                  payload: e.currentTarget.value,
                });
              }}
            />
          </Form.Group>
          <Form.Group className="mb-4" controlId="address2">
            <Form.Label>Dirección 2 (piso y puerta si procede)</Form.Label>
            <Form.Control
              type="text"
              value={address2}
              onChange={(e) => {
                dispatch({
                  type: 'FIELD',
                  fieldName: 'address2',
                  payload: e.currentTarget.value,
                });
              }}
            />
          </Form.Group>
          <Form.Group className="mb-4" controlId="city">
            <Form.Label>Municipio</Form.Label>
            <Form.Control
              type="text"
              value={city}
              onChange={(e) => {
                dispatch({
                  type: 'FIELD',
                  fieldName: 'city',
                  payload: e.currentTarget.value,
                });
              }}
            />
          </Form.Group>
          <Form.Group className="mb-4" controlId="province">
            <Form.Label>Provincia</Form.Label>
            <Form.Control
              type="text"
              value={province}
              onChange={(e) => {
                dispatch({
                  type: 'FIELD',
                  fieldName: 'province',
                  payload: e.currentTarget.value,
                });
              }}
            />
          </Form.Group>
          <Form.Group className="mb-4" controlId="postcode">
            <Form.Label>Código Postal</Form.Label>
            <Form.Control
              type="text"
              value={postcode}
              onChange={(e) => {
                dispatch({
                  type: 'FIELD',
                  fieldName: 'postcode',
                  payload: e.currentTarget.value,
                });
              }}
            />
          </Form.Group>
          <div className="mb-3">
            <Button variant="primary" type="submit">
              Guardar Cambios
            </Button>
          </div>
        </Form>
      )}
    </div>
  );
}

How can I make possible to retrieve current user info data and let them change the input values?

After a while trying to fix it and understand the problem, I also tried to look for an already posted question but did not succeded.

Thanks!

>Solution :

I am assuming the state is of the form

{
 isLoading: false,
 error: null,
 user: {
    name,
    surname,
    ...
 }
}

then I feel the reducers ‘FIELD’ case could be

case 'FIELD':
      return { ...state, user: { ...state.user, [action.fieldName]: action.payload } };

to make state updates easier, you might want to try useImmerReducer

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading