Home > front end >  How to parse object to another component using fetch
How to parse object to another component using fetch

Time:09-06

I need to be able to parse obj to another component called GuestForm.

However when i try to set obj.first_name i can see in the console that the the obj.first_name value is empty.

On top of having the object empty i would like to parse it to the component.

import React, { Component, useState, useEffect }  from 'react';
import GuestForm from '../../components/Guests/GuestForm.js';
import { useParams } from 'react-router-dom';
import axios from "axios";

function Edit() {
  
  const { id } = useParams();
  const [mode, setMode] = useState('edit');
  const [successMessage, setsuccessMessage] = useState('The guest has been edited successfully!');
  const [action, setAction] = useState('/guests/edit');
  const obj = {first_name: '', last_name: '', email: '', password: ''};

  const headers = {
    'Content-Type': 'application/json;charset=UTF-8',
    "Access-Control-Allow-Origin": "*", 
    "Accept": "application/json"
  }

  const res = fetch(process.env.REACT_APP_API_URL   action, {
    method: 'POST',
    headers: headers,
    body: JSON.stringify({data: {id: id}}),
   })
   .then((response) => response.json())
   .then((responseJson) => {
     //return responseJson.json.guest;
     obj.first_name = responseJson.json.guest.first_name;
   })
   .catch((error) => {
     console.error(error);
   });

   console.log(obj); // Empty value for first name here...

  return (
    <>
        <div className="container">
            <GuestForm mode={mode} successMessage={successMessage} obj={obj} action={action} />
        </div>
    </>
  );
}

export default Edit;

GuestForm

Here the component GuestForm which should display first name value in the field

import React, { Component, useState, useEffect }  from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { Link, useHistory } from 'react-router-dom';

// react-bootstrap components
import {
  Button,
  Card,
  Form,
  Row,
  Col,
} from "react-bootstrap";
import axios from "axios";

import { toast } from 'react-toastify';

function GuestForm({mode, successMessage, obj, action}) {

  const history = useHistory();

  const [details, setDetails] = useState([]);
  const [loading, setLoading] = useState(false);

  const [first_name, setFirstName] = useState(obj.first_name);
  const [last_name, setLastName] = useState(obj.last_name);
  const [email, setEmail] = useState(obj.email);
  const [password, setPassword] = useState(obj.password);

  const handleSave = e => {
      e.preventDefault();
      setLoading(true);
      axios({
        method: "POST",
        url: process.env.REACT_APP_API_URL   action,
        headers: { 'Content-Type': 'application/json;charset=UTF-8', "Access-Control-Allow-Origin": "*", "Accept": "application/json" },
        data: {
          data: obj
        }
      }).then(result => {
        if(result.data.json.error == false) {
            toast(successMessage, {
                position: "top-right",
                autoClose: 5000,
                hideProgressBar: false,
                closeOnClick: true,
                pauseOnHover: true,
                draggable: true,
                progress: undefined,
            });
            history.push('/dashboard/guests');
        }
        setDetails(result.data.json);
        setLoading(false);
      });
  };

  return (
    <>
    <div className="container">

        <div >
            <div >
                <h1 className="mt-0 mb-4 green-color">{mode == 'edit'? <span>Edit</span>: 'New' } Guest</h1>
            </div>
        </div>

        <Form onSubmit={handleSave} autoComplete="off">
            <div >
                <div >
                    <Form.Group>
                        <label htmlFor="exampleInputEmail1">
                        Email Address
                        </label>
                        <Form.Control
                        value={email} 
                        onChange={e => setEmail(e.target.value)}
                        type="email"
                        autoComplete="off"
                        ></Form.Control>
                    </Form.Group>
                </div>
                <div >
                <Form.Group>
                        <label>Password</label>
                        <Form.Control
                        value={password} 
                        onChange={e => setPassword(e.target.value)}
                        type="password"
                        autoComplete="new-password"
                        ></Form.Control>
                    </Form.Group>
                </div>
            </div>
            <div >
                <div >
                    <Form.Group>
                        <label>First Name</label>
                        <Form.Control
                        value={first_name} 
                        onChange={e => setFirstName(e.target.value)}
                        type="text"
                        autoComplete="off"
                        ></Form.Control>
                    </Form.Group>
                </div>
                <div >
                    <Form.Group>
                        <label>Last Name</label>
                        <Form.Control
                        value={last_name} 
                        onChange={e => setLastName(e.target.value)}
                        type="text"
                        autoComplete="off"
                        ></Form.Control>
                    </Form.Group>
                </div>
            </div>
            
            {(details.guest && details.error ) && <div className="error-message mt-4 mb-1">{details.message}</div>}

            <Button
                className="btn-fill pull-right mt-3"
                type="submit"
                variant="info"
                disabled={loading}
            >
            {loading && <span>{mode == 'edit'? <span>SAVE CHANGES</span>: 'ADD' }...</span>}
            {!loading && <span>{mode == 'edit'? <span>SAVE CHANGES</span>: 'ADD' }</span>}
            </Button>
            <div className="clearfix"></div>
        </Form>
      </div>
    </>
  );
}

export default GuestForm;

CodePudding user response:

The reason your console.log is showing up as empty is because you are setting the value of obj.first_name in an asynchronous callback, but the actual logging line will be executed synchronously before that asynchronous callback is called. If you were to instead add another .then to the chain and do the console.log in there, you would see the updated value. Here's a snippet that demonstrates what I mean:

const obj = { a: 'b' };

Promise.resolve()
  .then(() => {
    obj.a = 'c';
  })
  .then(() => {
    console.log('async:', obj);
  });
  
console.log('sync:', obj);

If you want to send this value to GuestForm, you'll have to use a state variable that will be updated once the fetch call finishes. You also want to wrap this fetch call in a useEffect, so that calling setObj doesn't result in an endless loop (the fetch call causes the state update, which then causes the component to be re-rendered, which causes the fetch call to rerun, and so on). Something like:

import React, { Component, useState, useEffect }  from 'react';
import GuestForm from '../../components/Guests/GuestForm.js';
import { useParams } from 'react-router-dom';
import axios from "axios";

function Edit() {
  
  const { id } = useParams();
  const [mode, setMode] = useState('edit');
  const [successMessage, setsuccessMessage] = useState('The guest has been edited successfully!');
  const [action, setAction] = useState('/guests/edit');
  const [obj, setObj] = useState({first_name: '', last_name: '', email: '', password: ''});

  const headers = {
    'Content-Type': 'application/json;charset=UTF-8',
    "Access-Control-Allow-Origin": "*", 
    "Accept": "application/json"
  }

  useEffect(() => {
    const res = fetch(process.env.REACT_APP_API_URL   action, {
      method: 'POST',
      headers: headers,
      body: JSON.stringify({data: {id: id}}),
     })
     .then((response) => response.json())
     .then((responseJson) => {
       //return responseJson.json.guest;
       const newObj = { ...obj, first_name: 
  responseJson.json.guest.first_name };
       setObj(newObj);
     })
     .catch((error) => {
       console.error(error);
     });
  }, []);

   console.log(obj); // This will now show the updated value (but will still have the default value on the initial render)

  return (
    <>
        <div className="container">
            <GuestForm mode={mode} successMessage={successMessage} obj={obj} action={action} />
        </div>
    </>
  );
}

export default Edit;

To use the updated value in GuestForm, you need to make sure your state variable is updated when the passed in prop is updated. This is best achieved with a useEffect. Add this to your GuestForm component

useEffect(() => {
  setFirstName(obj.first_name);
}, [obj]);

This is necessary because you're duplicating the prop value with state variables in the child component. A more common pattern would be to pass both obj and setObj as props to GuestForm so that in the child you can modify the parent's state variable directly without creating a copy

  • Related