Mr.Andor
Mr.Andor

Reputation: 25

Trying to get data from child of a child in ReactJS

I’ve read that you can’t really pass props upwards like that, but you can do so through functions. I went and found a workaround and it worked.

My problem is: I’m trying to get data to App from 2 layers down - App > DataInput > ValidateUser - so I can pass it over from App to 2 other different components that have nothing to do with filling the form.

I can get the data from ValidateUser back to DataInput, just before sending it to App I’m logging the result and it’s all as expected. The problem begins when I try to send it to App and the data I receive is undefined.

I checked a few times to see if I was making a typo or logical error while implementing the second data call. Unless I completely missed it, nothing. I started to think that, maybe, then, the problem might be with the execution order. App is read first so, maybe, it wasn’t getting updated once I assigned the value further down the execution line? But then, I’m updating the state when I click the button and it prints out the undefined and the blank object again being called from App while I can see the object is fully filled when it’s last called over the handler in DataInput…

I’m probably missing something here.

App

import "./App.css";
import Box from "@mui/material/Box";
import React from "react";
import DataInput from "./components/DataInput";
import UserDataTable from "./components/UserDataTable";

const App = () => {
  let userData;
  let formSubmited = false;

  const getUserData = (params) => {
      console.log(params);
      userData = { ...userData, ...params };
      console.log(userData);
  };

  return (
    <div className="App">
      <Box
        sx={{
          width: 1000,
          height: 600,
          backgroundColor: "rgba(197, 202, 255, 1)",
          m: 4,
          border: 1.4,
          borderColor: "rgba(140, 150, 255, 1)",
        }}
      >
        <DataInput sendData={getUserData} />

        <UserDataTable />

        <div className="Author">
          <h3>André Lourenço</h3>
        </div>
      </Box>
    </div>
  );
};

export default App;

DataInput

import {
  TextField,
  Select,
  MenuItem,
  FormControl,
  InputLabel,
} from "@mui/material";
import { LoadingButton } from "@mui/lab";
import React, { useRef, useState } from "react";
import SaveIcon from "@mui/icons-material/Save";
import "../styles/DataInput.css";
import ValidateUser from "./ValidateUser";

export default function DataInput(props) {
  //State & Ref Hooks
  const [country, setCountry] = useState("");

  const handleCountryChange = (event) => setCountry(event.target.value);

  const countryInputRef = useRef();
  const nameInputRef = useRef();
  const surnameInputRef = useRef();
  const birthdayInputRef = useRef();

  const [rawData, setRawData] = useState("");

  // I/O
  let returnUserData;

  const handleFormSubmission = (event) => {
    event.preventDefault();

    let data = {
      name: "",
      surname: "",
      country: "",
      birthday: "",
    };

    data.name = nameInputRef.current.value;
    data.surname = surnameInputRef.current.value;
    data.country = countryInputRef.current.value;
    data.birthday = birthdayInputRef.current.value;

    setRawData(data);
  };

  const getValidatedData = (params) => {
    returnUserData = { ...returnUserData, ...params };
    returnUserData.isSubmited = true;
    console.log(returnUserData);
  };

  const handleSendData = (data) => props.sendData(data);

  return (
    <div className="DataInput">
      <form>
        <div className="Input-Boxes">
          <div className="Box-Name">
            <TextField
              sx={{ input: { color: "blue" } }}
              label="Name"
              inputRef={nameInputRef}
              required
            />
          </div>

          <div className="Box-Surname">
            <TextField
              sx={{ input: { color: "blue" } }}
              label="Surname"
              inputRef={surnameInputRef}
              required
            />
          </div>

          <div className="Box-Country">
            <FormControl variant="filled" sx={{ minWidth: 220 }}>
              <InputLabel id="demo-simple-select-filled-label">
                Countries
              </InputLabel>

              <Select
                required
                labelId="demo-simple-select-filled-label"
                id="demo-simple-select-filled"
                label="Countries"
                value={country}
                autoWidth
                onChange={handleCountryChange}
                inputRef={countryInputRef}
              >
                <MenuItem value={"Brazil"}> Brazil </MenuItem>
                <MenuItem value={"Portugal"}> Portugal </MenuItem>
              </Select>
            </FormControl>
          </div>

          <div className="Box-Birthday">
            <TextField
              sx={{ input: { color: "blue" } }}
              label="Birthday"
              inputRef={birthdayInputRef}
              type="date"
              InputLabelProps={{ shrink: true }}
              required
            />
          </div>
        </div>

        <div className="Button-Save">
          <LoadingButton
            loadingPosition="end"
            endIcon={<SaveIcon />}
            variant="outlined"
            type="submit"
            onClick={handleFormSubmission}
          >
            Save
          </LoadingButton>
          <ValidateUser data={rawData} sendData={getValidatedData} />
        </div>
      </form>

      {handleSendData(returnUserData)}
    </div>
  );
}

Upvotes: 0

Views: 255

Answers (3)

Code-Cody-418
Code-Cody-418

Reputation: 121

Trying to find a work around to get data back up to the parent components has never worked to well for me. I would recommend using a library such as Redux. With Redux you can put data in its current state into a Store. This data can then be called into any component within your React App. React-Redux has hooks that allows you to get this data. Although Redux does take a good amount of set-up and a learning curve it might work for your projects needs. Redux-Toolkit can help with some of the set-up.

Some tips on using Redux. Redux wraps your React app, so make sure that you don't miss this step. Your gonna need a Store that will hold the current state of your data. Your gonna use hooks to store and get your data. Primarily the useDispatch hook to update your Store data and useSelector to grab your data.

Once you have Redux all set-up you can do useDispatch(returnUserData). To save your data to the store. You could then call the data into App.js using let data = useSelector(state => state.data.setdata). Of course what is actually inside of useSelector needs to match your Store set-up.

Upvotes: 1

Priyank Kachhela
Priyank Kachhela

Reputation: 2635

Your implementation to send data to App component is correct. But you don't need handleSendData function and you are calling it from return which is wrong. You can send data to App from inside getValidatedData function like below.

const getValidatedData = (params) => {
   returnUserData = { ...returnUserData, ...params };
   returnUserData.isSubmited = true;
   console.log(returnUserData);
   props.sendData(returnUserdata)

 };

Upvotes: 1

Related Questions