1

I've the following component structure.

<Dashboard>
   <UserData />
   <MealList />
   <Search />
</Dashboard>

I'm fetching the contents for UserData and MealList using useEffects inside dashboard and passing them down as props.

const [username, setUsername] = useState("");
    const [email, setEmail] = useState("");
    const [calories, setCalories] = useState("");
    const [meals, setMeals] = useState([]);

 useEffect(async () =>{
        const data = await fetchUserData(localStorage.getItem('email'));
        localStorage.setItem('user_id', data['id']);
        setUsername(data['username']);
        setEmail(data['email']);
        setCalories(data['calories']);
        const mealsData = await fetchMeals(localStorage.getItem('user_id'));
        setMeals([...meals, ...mealsData]);

    }, []);

Now what I want to implement is based on search parameters the meal list refreshes.

I'm grabbing the search parameters from the Search component and passing it back to the MealList.

Then I make a REST call and fetch the updated data.

const updateList = async ( ft, tt, fd, td) =>{
        const userId = localStorage.getItem('user_id');
        const updatedMealsList = await searchMeals(userId, ft, tt, fd, td);
        console.log('Updated Meals', updatedMealsList);
        setMeals([]);//Tried this but doesn't work
        setMeals([...meals, ...updatedMealsList]);//
    };

My question is how do I update the Meallist array and re-render the component?

I've already tried this, and it doesn't work. What I mean by doesn't work is neither is data in the meals array updated nor is it being re-rendered.

Here are the components.

const Dashboard = () => {
    const [username, setUsername] = useState("");
    const [email, setEmail] = useState("");
    const [calories, setCalories] = useState("");
    const [meals, setMeals] = useState([]);

    useEffect(async () =>{
        const data = await fetchUserData(localStorage.getItem('email'));
        localStorage.setItem('user_id', data['id']);
        setUsername(data['username']);
        setEmail(data['email']);
        setCalories(data['calories']);
        const mealsData = await fetchMeals(localStorage.getItem('user_id'));
        setMeals([...meals, ...mealsData]);

    }, []);


    const updateList = async ( ft, tt, fd, td) =>{
        const userId = localStorage.getItem('user_id');
        const updatedMealsList = await searchMeals(userId, ft, tt, fd, td);
        setMeals([]);
        console.log('Updated Meals', updatedMealsList);
        setMeals([...meals, ...updatedMealsList]);
    };
    console.log('Updated Meals', meals);
    return (
        <div>
            <Navigation/>
            <div className="wrapper">
                <div style={{display: 'flex', flexFlow: 'row wrap'}}>
                    <UserData username={username} email={email} calories={calories}/>
                    <Search onSubmit={updateList}/>
                </div>
                <MealList meals={meals}/>

            </div>

        </div>

    )
};
export default Dashboard;

Meallist

const MealList = (props) => {
    return (
        <div style={{width: '70%'}}>
            <h3 style={{color: 'grey', fontSize: '1.4em', fontWeight: '700', marginBottom: '1em'}}><FontAwesomeIcon
                icon={faPlusCircle} style={{color: '#007bff', marginRight: '0.5em'}}/> ADD MEAL</h3>
            <Table striped bordered hover>
                <thead>
                <tr>
                    <th>#</th>
                    <th>Meal</th>
                    <th>Calories</th>
                    <th>Time</th>
                    <th>Date</th>
                    <th>Update</th>
                    <th>Delete</th>
                </tr>
                </thead>
                <tbody>
                {props.meals.map((meal, index) => (<Meal count={index +1} meal={meal}/>))}
                </tbody>
            </Table>
        </div>

    )
};
export default MealList;
13
  • On the first example, where did you get the meals array because I have seen only mealsData? Also try to use React dev tools to inspect the aforementioned arrays/data. Commented Dec 30, 2019 at 22:04
  • Are you passing the setMeals function down as a prop from Dashboard to the Meallist component? From reading the question I'm assuming the updateList call is coming from there... once you pass the function down to the correct component it should work just fine. Commented Dec 30, 2019 at 22:06
  • @Edper I've updated the questions with the useState which brings the meals in context. Commented Dec 30, 2019 at 22:07
  • @Ryan No, I'm using the setMeals to update the meals array in the Dashboard and then passing the updated state down. Commented Dec 30, 2019 at 22:08
  • 1
    Giving useEffect an async function should be giving a warning. Commented Dec 30, 2019 at 22:11

1 Answer 1

1

First, I advise you to read the following article

https://overreacted.io/a-complete-guide-to-useeffect/

this explains in detail how to properly use the useEffect hook

let dynamicMealId = 3;
function delay(payload) {
  return new Promise((resolve) => {
    window.setTimeout(() => {
      resolve(payload)
    }, 350)
  })
}

function fetchUserData(email) {
  return delay({
    id: Date.now(),
    username: '[email protected]',
    calories: 2000
  })
}

function fetchMeals(userId) {
  return delay([{
    id: 1,
    name: 'Meal 1'
  }, {
    id: 2,
    name: 'Meal 2'
  }, {
    id: 3,
    name: 'Meal 3'
  }])
}

function searchMeals(userId, filters) {
  return delay(Array.from({length: 3}, (v, k) => k+1).map(function () {
    const meal = {
      id: dynamicMealId,
      name: 'Meal ' +  dynamicMealId
    }
    dynamicMealId++;
    return meal;
  }))
}

function UserData(props) {
   return (
     <div>
       {props.user ? (
          <React.Fragment>
            <h1>UserData</h1>
            <div>{props.user.username}</div>
            <div>{props.user.calories}</div>
          </React.Fragment>
        ) : (
          <span>Loading User...</span>
        )}
     </div>
   );
}

function Search(props) {
  
  function handleSubmit(ev) {
    ev.preventDefault()
    props.onSearch(ev.target.elements[0].value);
    
  }
  
  return (
    <form onSubmit={handleSubmit}>
      <h1>Search</h1>
      <label forHtml="input">
        Enter Keyword
      </label>
      <input type="text" id="input"/>
    </form>
  )
}

function MealList(props) {
  console.log(props.meals)
  return (
    <div>
      <h1>MealList</h1>
      <ul>
        {props.meals.map(function(meal) {
         return (
           <li key={meal.id}>{meal.name}</li>
         )
        })}
      </ul>
    </div>
  )
}


const Dashboard = () => {
  const [user, setUser] = React.useState(null)
  const [meals, setMeals] = React.useState(null)
  const [filters, setFilters] = React.useState(null)
  
  // loads user data
  React.useEffect(() => {
     function loadData() {
       fetchUserData('[email protected]')
         .then((userData) => {
           setUser(userData);
           return fetchMeals(userData.id)
         })
         .then((userMeals) => {
           setMeals(userMeals);
         });
    }
    
    loadData();
  }, []);
  
  // loads new meals
  React.useEffect(() => {
    if (!user || !filters) { return; }
    
    function search() {
      searchMeals(user.id, filters)
        .then(newMeals => {
          setMeals(currentMeals => [
            ...currentMeals,
            ...newMeals
          ])
        })
    }
    
    search();
  }, [user, filters]);
  

  
  return (
    <div>
      Dashboard
      <UserData user={user}/>
      {user && meals && (
        <div>
          <Search onSearch={newFilters => setFilters(newFilters)} />
          <MealList 
            meals={meals}
          />
        </div>
      )}
    </div>
  )
}



ReactDOM.render(
  <Dashboard />,
  document.querySelector('#app')
)
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.11.0/umd/react.production.min.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.11.0/umd/react-dom.production.min.js"></script>
</head>
<body>
  <div id="app"></div>
</body>
</html>

Sign up to request clarification or add additional context in comments.

2 Comments

Search is a sibling of MealList, so the communication between the two can only happen inside the Dashboard. You might wanna read the question again.
I couldn't get this to work, can you please take a look at my repo, github.com/melissa-stewart/mealplan

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.