Skip to content

Using React Hooks, when I pass down a prop from parent to a child component, the prop in the child component is undefined

What am I trying to do?

I’m trying to set an array of objects in which the value within the array is dependent on the parent component.

What is the code that currently tries to do that?

Here are the different files simplified:

// Parent.

export default function Parent() {
  const [filePaths, setFilePaths] = useState();

  useEffect(() => {
    var fileContent = JSON.parse(fs.readFileSync("./config.json"); // Reading from a JSON.
    var tempFilePaths = [];
    fileContent.FilePaths.forEach((file) => {
      tempFilePaths.push(file);
    });
    setFilePaths(tempFilePaths); // Contents of "config.js" is now in the "useState".
  }, []);

  return (
    <Child filePaths={filePaths}/>
  )
}
// Child.

export default function Child({filePaths}) {
  var links = [
    {
      path: filePaths[0].Link1,
    },
    {
      path: filePaths[0].Link2,
    },
  ]

  return (
    <div>Nothing here yet, but I would map those links to front-end links.</div>
  )
}
// config.json

{
  "url": "http:localhost:3000",
  "FilePaths": [
    {
      "Link1": "C:DocumentsSomething",
      "Link2": "C:DocumentsSomethingElse"
    }
  ]
}

When I render the “filePaths” in the return() of the Child component, the “filePaths” is able to be rendered, but I wish to set the “filePaths” to the variable “links”.

What do I expect the result to be?

I expect the variable “links” to be fine in the child component, being able to be used within the child component.

What is the actual result?

When starting the app I get a TypeError: Cannot read property '0' of undefined.

What I think the problem could be?

I think the child component renders without the parent component finishing the useEffect(). I’m wondering if there’s a way to tell the child component to wait for the parent component to finish, then proceed with setting the variable of “links”.

Answer

filePaths will be undefined because you call useState() with empty input.

There are two options (you can choose one) to solve this:

  1. Initialize filePaths inside the useState()

  2. Return the Child component if the filePaths is not null/undefined.

export default function Parent() {
    const [filePaths, setFilePaths] = useState();

    useEffect(() => {
        var fileContent = JSON.parse(fs.readFileSync("./config.json"); // Reading from a JSON.
        var tempFilePaths = [];
        fileContent.FilePaths.forEach((file) => {
            tempFilePaths.push(file);
        });
        setFilePaths(tempFilePaths); // Contents of "config.js" is now in the "useState".
    }, []);

    return (
        // return the Child component if the filePaths is not null/undefined
        {filePaths && <Child filePaths={filePaths}/>}
    )
}

I personally prefer the second one because we can add a loading component when the filePaths is still null/undefined.