2
votes

I'm a bit of a newbie with React functional components, I have a child and parent components with some state that gets updated with useEffect, which state apparently resets back to its initial values after render.

Parent has a list of users it passes to its child:

Parent:

const Parent = () => {
   const [users, setUsers] = useState([])
   const getUsers = () => {
   setUsers(["pedro", "juan"])
  }
    useEffect(() => {
        getUsers() 
    }, []);

   return <div> 
    <Child users={users} /> 
}

Child:

const Child = () => {
   const [users, setUsers] = useState([])
  useEffect(() => {
    setUsers(props.users)
  }, [[...props.users]]);
}

If I for any reason try to access state (users) from either my child or parent components I get my initial value, which is an empty array, not my updated value from getUsers(), generally with a Parent Class component I'd have no trouble accessing that info, but it seems like functional components behave diffently? or is it caused by the useEffect? generally I'd use a class component for the parent but some libraries I use rely on Hooks, so I'm kind of forced to use functional components.

2

2 Answers

1
votes

There are a couple of mistakes the way you are trying to access data and passing that data.

You should adapt to the concept of lifting up state, which means that if you have users being passed to your Child component, make sure that all the logic regarding adding or removing or updating the users stays inside the Parent function and the Child component is responsible only for displaying the list of users.

Here is a code sandbox inspired by the code you have shared above. I hope this answers your question, do let me know if otherwise.

Also sharing the code below.

import React, { useState } from "react";

export default function Parent() {
  const [users, setUsers] = useState([]);
  let [userNumber, setUserNumber] = useState(1); // only for distinctive users, 
    //can be ignored for regular implementation

  const setRandomUsers = () => {
    let newUser = {};
    newUser.name = `user ${userNumber}`;
    setUsers([...users, newUser]);
    setUserNumber(++userNumber);
  };

  return (
    <div className="App">
      <button onClick={setRandomUsers}>Add New User</button>
      <Child users={users} />
   </div>
  );
}

const Child = props => {
  return (
    props.users &&
    props.users.map((user, index) => <div key={index}>{user.name}</div>)
  );
};
0
votes

it doesnt make sense to me that at Child you do const [users, setUsers] = useState([]). why dont you pass down users and setUsers through props? your child's setUser will update only its local users' state value, not parent's. overall, duplicating parent state all around its children is not good, you better consume it and updating it through props.

also, once you do [[...props.users]], you are creating a new array reference every update, so your function at useEffect will run on every update no matter what. useEffect doesnt do deep compare for arrays/objects. you better do [props.users].