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
28 changes: 27 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,30 @@ This project was bootstrapped with [Create React App](https://github.com/faceboo
## Setup
- 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
- Run `npm run start` to start the local development server

## 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, 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 smart, 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. 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.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-scripts": "5.0.1",
Expand Down
18 changes: 2 additions & 16 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import './App.css';
import Character from './components/character';
import charactersData from './charactersData';
import CharacterList from './components/character-list';
// import something from 'some place'

// Creating a new function component in the same file
Expand All @@ -23,23 +24,8 @@ function App() {
{ /* The below array iterator (map), 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 */}
{
charactersData.map((character) => {
return (
<Character
name={character.name}
birthday={character.born}
blood={character.blood}
imgUrl={character.imgUrl}
quote={character.quote}
/>
)
})
}
<CharacterList />
{ /* Using array iterator (map) instead of repeating 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} />
</div>
);
}
Expand Down
48 changes: 48 additions & 0 deletions src/components/character-list.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import Character from "./character";
import axios from 'axios';
import {useEffect, useState} 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');
console.log(response.data);
setCharacters(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 getCharactersComponents = () => {
return characters.map((character, index) => {
return (
<Character
key={index}
name={character.name}
birthday={character.born}
blood={character.blood}
imgUrl={character.imgUrl}
quote={character.quote}
/>
)
})
}

return (
// if characters data is not null (the initial value of the characters state variable)
// then I want to show Characters components
// else I want to show "loading..."
characters ? getCharactersComponents() : 'Loading...'
)
}

export default CharacterList;
4 changes: 3 additions & 1 deletion src/components/character.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// You can import other components and use them too!
import Image from './image';
import LikeCounter from './like-counter';

// A React component is just a function that returns some JSX
// Specifically, it returns some JSX with only one parent element
Expand All @@ -8,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: <Character name="Luna" blood="Pure-blood" birthday="Feb 8" quote="Some quote" imgUrl="someurl.com" />
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 (
Expand All @@ -21,6 +22,7 @@ const Character = (props) => {
<h2>Quote</h2>
<p>{props.quote}</p>
<Image url={props.imgUrl} />
<LikeCounter />
<hr />
</>
)
Expand Down
35 changes: 35 additions & 0 deletions src/components/like-counter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import {useState} from 'react';
import '../styles/like-counter.css';

const LikeCounter = () => {
const [count, setCount] = useState(0);
const [favorite, setFavorite] = useState(false);

const whenIncreaseButtonClicked = () => {
setCount(count + 1);
};
const whenDecreaseButtonClicked = () => {
if (count > 0) {
setCount(count - 1);
}
}

return (
<>
<div>
<h4>Number of likes: {count}</h4>
<button className={'buttonStyle'} onClick={whenIncreaseButtonClicked}>Like me!</button>
<button onClick={whenDecreaseButtonClicked}>Decrease likes</button>
</div>
<div>
<h4>{favorite && '⭐️'}</h4>
<button onClick={() => {
console.log('favorite button clicked')
setFavorite(!favorite);
}}>{favorite ? 'Un-favorite' : 'Favorite'}</button>
</div>
</>
)
}

export default LikeCounter;
4 changes: 4 additions & 0 deletions src/styles/like-counter.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.buttonStyle {
padding: 10px;
background-color: pink;
}