From 02190adc121b47a63e3bba904abaa5b55b015900 Mon Sep 17 00:00:00 2001 From: tessawiedmann Date: Fri, 19 Jan 2024 13:40:56 +0100 Subject: [PATCH 1/3] lifting count state --- src/components/character-list.js | 60 +++++++++++++++++++++++++++++--- src/components/character.js | 9 +++-- src/components/counter.js | 18 +++++----- 3 files changed, 72 insertions(+), 15 deletions(-) diff --git a/src/components/character-list.js b/src/components/character-list.js index a373903..0d661f2 100644 --- a/src/components/character-list.js +++ b/src/components/character-list.js @@ -7,12 +7,22 @@ import { useState, useEffect } from "react"; const CharacterList = () => { const [characters, setCharacters] = useState(null); + // Get the characters data from the API const getCharacters = async () => { const response = await axios.get( "https://my-json-server.typicode.com/TechmongersNL/fs03-react/characters" ); - setCharacters(response.data); - console.log(response.data); + + // We want to keep track of the number of "likes" for each character in our state now too + // Add our own "likes" key into each character object + const charactersWithLikes = response.data.map((character) => { + return { ...character, likes: 0 }; + }); + console.log("without likes:", response.data); + console.log("with likes:", charactersWithLikes); + + // Each character, now with data from the API and our own "likes" data, is saved into the local React state + setCharacters(charactersWithLikes); }; // If you don't put getCharacters in a useEffect hook, getCharacters will be called (and will make an Axios request) every time CharactersList gets re-rendered @@ -24,6 +34,25 @@ const CharacterList = () => { getCharacters(); }, []); + const increaseLikes = (id) => { + // when this function is called, I want to increase the amount of likes on that character + // in order to update a character, I need to use setCharacters + + // to update the character: first we need to find the character that we want to update + // then make a copy of that character, and increase the amount of likes in it + // add that updatedCharacter back into our updatedArray: setCharacter(updatedArray) + + // we can also do the above logic with just one single .map: + const updatedArray = characters.map((character) => { + if (character.id === id) { + return { ...character, likes: character.likes + 1 }; + } else { + return character; + } + }); + setCharacters(updatedArray); + }; + const charactersComponents = () => { return characters.map((character) => { return ( @@ -34,19 +63,42 @@ const CharacterList = () => { blood={character.blood} imgUrl={character.imgUrl} quote={character.quote} + likes={character.likes} + increaseLikes={increaseLikes} + id={character.id} /> ); }); }; + const getTotalLikes = () => { + let total = 0; + characters.forEach((character) => { + total = total + character.likes; + }); + return total; + + // The code below uses .reduce and does the same thing as above: + // total = characters.reduce((total, character) => { + // return total + character.likes + // }, 0) + // return total; + }; + // If we do the below, we will get an error saying something like "cannot map on null", because initially characters is null! // return
{charactersComponents()}
; - // To fix this, we add a ternary conditional: // If characters data is not null (which is the initial value of the characters state variable) // then I want to show Characters components // else I want to show "loading..." - return
{characters ? charactersComponents() : "Loading..."}
; + return ( +
+

+ Total number of likes: {characters ? getTotalLikes() : "Loading..."} +

+ {characters ? charactersComponents() : "Loading..."} +
+ ); }; export default CharacterList; diff --git a/src/components/character.js b/src/components/character.js index 912f056..a2a30d0 100644 --- a/src/components/character.js +++ b/src/components/character.js @@ -9,7 +9,7 @@ import Image from "./image"; // the keys of the props object match the attributes added into the component when you use it // Character component usage: const Character = (props) => { - console.log(props); + // console.log(props); // React requires you return only one parent element // You can use a React Fragment (an empty html element) to fix this issue return ( @@ -23,7 +23,12 @@ const Character = (props) => {

{props.quote}

{/* using a component within a component also works! */} - + {/* Passing props from CharacterList even further down into Counter */} +
); diff --git a/src/components/counter.js b/src/components/counter.js index 5d32a9c..b5181f6 100644 --- a/src/components/counter.js +++ b/src/components/counter.js @@ -1,13 +1,10 @@ import { useState } from "react"; -const Counter = () => { - // count and favorite are now special React state variables! - // When the value of count or favorite changes (by using setCount or setFavorite), React will re-render the Counter component - const [count, setCount] = useState(0); +const Counter = (props) => { + // No more local state for count, it's now being passed in through props + // const [count, setCount] = useState(0); + const [favorite, setFavorite] = useState(false); - // ^ ^ ^ - // state variable ^ initial value of the state variable - // function to update the state variable const favoriteClicked = () => { setFavorite(!favorite); @@ -15,11 +12,14 @@ const Counter = () => { return (
-

Likes: {count}

+

Likes: {props.likes}