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
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,44 @@ This project was created with [Create React App](https://github.com/facebook/cre
- Git clone this repo
- Run `npm install` to install all the dependencies listed in package.json
- Run `npm run start` to start the local development server

## Branches

Check out the different branches to see how this repo evolved over a week of React lectures

- (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

## What are React Hooks?

- Functions that let you perform a specific task inside React, like "hooking into" React state or component lifecycles
- Start with the word "use" -- useState and useEffect for example
- Pieces of code (functions) that someone wrote
- Hooks can be inside React or you can import them from somewhere else (like Redux that we'll learn next week), but today we'll focus on just useState and useEffect from React
- You can write your own hooks (custom hooks)

## useState

- A hook that creates state variables that React listens to. It "reacts" when the values of these state variables change
- Used when we have variables whose value should change when an event happens (user generated)
- When the value of the state variable changes, React re-renders the component in a efficient, optimized way for you

## useEffect

- A hook thats used when you want React to re-render without user interaction
- Used to fetch data (async)
- Helps control the amount of re-renders on the page

Fetching data from API flow

1. Write an async function
2. Make a request with axios
3. Console.log what I'm getting back to make sure it's what I expect (and don't forget to call the function)
4. Import useEffect from 'react'
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
29 changes: 29 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.5",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
Expand Down
46 changes: 3 additions & 43 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,54 +1,14 @@
import "./App.css";
import Character from "./components/character";
import charactersData from "./charactersData";
import CharacterList from "./components/character-list";
// import something from 'some place'

// App.js is the entry point to the rest of your app
// This is default behavior for create-react-app
function App() {
const charactersComponents = () => {
return charactersData.map((character) => {
return (
<Character
name={character.name}
birthday={character.born}
blood={character.blood}
imgUrl={character.imgUrl}
quote={character.quote}
/>
);
});
};

return (
<div className="App">
{/* If we want a Character component for each character in our data, we could repeat it like below: */}
<Character
name={charactersData[0].name}
birthday={charactersData[0].born}
blood={charactersData[0].blood}
imgUrl={charactersData[0].imgUrl}
quote={charactersData[0].quote}
/>
<Character
name={charactersData[1].name}
birthday={charactersData[1].born}
blood={charactersData[1].blood}
imgUrl={charactersData[1].imgUrl}
quote={charactersData[1].quote}
/>
<Character
name={charactersData[2].name}
birthday={charactersData[2].born}
blood={charactersData[2].blood}
imgUrl={charactersData[2].imgUrl}
quote={charactersData[2].quote}
/>
{/* ... and so on. But this gets repetitive really fast!! We can use .map to fix this */}
{/* The below function uses an array iterator (map), which loops over every element in the charactersData array */}
{/* The result of map is another array with new data */}
{/* In our case, this new data is a Character component */}
{charactersComponents()}
{/* Now have a new CharactersList component, to keep our App component more clean and organized */}
<CharacterList />
</div>
);
}
Expand Down
52 changes: 52 additions & 0 deletions src/components/character-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import Character from "./character";
import axios from "axios";
import { useState, useEffect } from "react";
// import charactersData from "../charactersData";
// ^ we don't have to import from charactersData anymore! We can now make a network request with axios

const CharacterList = () => {
const [characters, setCharacters] = useState(null);

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

// 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
// We only want getCharacters to be called once, the first time getCharacters is rendered, which we can do by using useEffect with an empty dependency array at the end
// Don't do this!:
//getCharacters();
// Instead, do this:
useEffect(() => {
getCharacters();
}, []);

const charactersComponents = () => {
return characters.map((character) => {
return (
<Character
key={character.id}
name={character.name}
birthday={character.born}
blood={character.blood}
imgUrl={character.imgUrl}
quote={character.quote}
/>
);
});
};

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

export default CharacterList;
2 changes: 2 additions & 0 deletions src/components/character.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// You can import other components and use them too!
import Counter from "./counter";
import Image from "./image";

// A React component is just a function that returns some JSX
Expand All @@ -22,6 +23,7 @@ const Character = (props) => {
<p>{props.quote}</p>
{/* using a component within a component also works! */}
<Image url={props.imgUrl} />
<Counter />
<hr />
</>
);
Expand Down
37 changes: 37 additions & 0 deletions src/components/counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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 [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>
<button
onClick={() => {
console.log("Like button was clicked!");
setCount(count + 1);
}}
>
Increase likes
</button>
<hr />
<button onClick={favoriteClicked}>
{favorite ? "Un-favorite" : "Favorite"}
</button>
{/* If favorite is true, show the star, otherwise, show nothing */}
{favorite ? "⭐️" : ""}
</div>
);
};

export default Counter;