最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

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

programmeradmin1浏览0评论

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 ponent.

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:\Documents\Something",
      "Link2": "C:\Documents\SomethingElse"
    }
  ]
}

When I render the "filePaths" in the return() of the Child ponent, 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 ponent, being able to be used within the child ponent.

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 ponent renders without the parent ponent finishing the useEffect(). I'm wondering if there's a way to tell the child ponent to wait for the parent ponent to finish, then proceed with setting the variable of "links".

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 ponent.

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:\Documents\Something",
      "Link2": "C:\Documents\SomethingElse"
    }
  ]
}

When I render the "filePaths" in the return() of the Child ponent, 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 ponent, being able to be used within the child ponent.

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 ponent renders without the parent ponent finishing the useEffect(). I'm wondering if there's a way to tell the child ponent to wait for the parent ponent to finish, then proceed with setting the variable of "links".

Share Improve this question edited Dec 26, 2021 at 21:38 ctrlaltdeleon asked Aug 11, 2021 at 23:17 ctrlaltdeleonctrlaltdeleon 4061 gold badge5 silver badges16 bronze badges 1
  • filePaths is undefined in the parent until it's set by setFilePaths(tempFilePaths); in the useEffect hook. It seems the consuming ponent assumes it's defined. Please share a plete code example that reproduces your issue. – Drew Reese Commented Aug 11, 2021 at 23:52
Add a ment  | 

4 Answers 4

Reset to default 2

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 ponent 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 ponent if the filePaths is not null/undefined
        {filePaths && <Child filePaths={filePaths}/>}
    )
}

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

You are right, that's why you should change your child ponent. It renders the filePaths whether it is defined or not.

Try to do as follows.

export default function Child({filePaths}) {
  const [links, setLinks] = useState(filePaths);
  useEffect(()=>{
    setLinks(filePaths);
  },[filePaths])

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

I think you're right on your guess about the sequence of methods calling:

According to this, when you use useEffect the method gets called after the rendering, as if it was a ponentDidMount lifecycle method, which is supported by the React official lifecycle diagram and React Documentation. And that is the reason because the props.filePaths whithin the Child ponent is undefined.

To avoid this, you should try to set an initial value (in the useState method).

something like the following (maybe extracting the repetition as a function):

// Parent.

export default function Parent() {

  var fileContent = JSON.parse(fs.readFileSync("./config.json"); // Reading from a JSON.
    var tempFilePaths = [];
    fileContent.FilePaths.forEach((file) => {
      tempFilePaths.push(file);
    });
  
  const [filePaths, setFilePaths] = useState(tempFilePaths);

  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}/>
  )
}

const [filePaths, setFilePaths] = useState();will initialize filePaths as undefined.
so you can check firstly like if (!filePaths) return null in the parent; or set initState in useState like @see https://reactjs/docs/hooks-reference.html#lazy-initial-state

const [filePaths, setFilePaths] = useState(()=> {
    var fileContent = JSON.parse(fs.readFileSync("./config.json");
    var tempFilePaths = [];
    fileContent.FilePaths.forEach((file) => {
      tempFilePaths.push(file);
    });
    return tempFilePaths;
});

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论