Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ Check out the different branches to see how this repo evolved over a week of Rea

- (main) React components & props
- (react-state) React state with useState & useEffect
- (react-lifting-state) React lifting state
- (react-router) Reacter router

You can see the changes from day to day in the ["Pull Requests"](https://github.com/TechmongersNL/fs04-react/pulls) in this repo

Expand Down Expand Up @@ -46,3 +48,7 @@ Fetching data from API flow
5. Call the async function inside useEffect
6. Check my console.log and put the data in the state
7. React will render something on the screen based on state

## Lifting state diagram

![Lifting state diagram](component-diagram.png)
Binary file added component-diagram.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
60 changes: 56 additions & 4 deletions src/components/character-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 (
Expand All @@ -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 <div>{charactersComponents()}</div>;

// 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 <div>{characters ? charactersComponents() : "Loading..."}</div>;
return (
<div>
<h2>
Total number of likes: {characters ? getTotalLikes() : "Loading..."}
</h2>
{characters ? charactersComponents() : "Loading..."}
</div>
);
};

export default CharacterList;
8 changes: 6 additions & 2 deletions src/components/character.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import Image from "./image";
// the keys of the props object match the attributes added into the component when you use it
// Character component usage: <Character name="Luna" blood="Pure-blood" birthday="Feb 8" quote="Some quote" imgUrl="someurl.com" />
const Character = (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 (
Expand All @@ -23,7 +22,12 @@ const Character = (props) => {
<p>{props.quote}</p>
{/* using a component within a component also works! */}
<Image url={props.imgUrl} />
<Counter />
{/* Passing props from CharacterList even further down into Counter */}
<Counter
likes={props.likes}
increaseLikes={props.increaseLikes}
id={props.id}
/>
<hr />
</>
);
Expand Down
18 changes: 9 additions & 9 deletions src/components/counter.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
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);
};

return (
<div>
<p>Likes: {count}</p>
<p>Likes: {props.likes}</p>
<button
onClick={() => {
console.log("Like button was clicked!");
setCount(count + 1);
// No more local state for count, it's now being passed in through props (increaseLikes)
// setCount(count + 1);

props.increaseLikes(props.id);
}}
>
Increase likes
Expand Down