From de36654168cee94c00015191625104728595c9fb Mon Sep 17 00:00:00 2001 From: tessawiedmann Date: Wed, 17 Jan 2024 13:29:11 +0100 Subject: [PATCH 1/6] finished useState --- README.md | 26 ++++++++++++++++++++++++++ package-lock.json | 29 +++++++++++++++++++++++++++++ package.json | 1 + src/App.js | 7 +++++-- src/components/character.js | 2 ++ src/components/counter.js | 34 ++++++++++++++++++++++++++++++++++ 6 files changed, 97 insertions(+), 2 deletions(-) create mode 100644 src/components/counter.js diff --git a/README.md b/README.md index e23524d..3ac8756 100644 --- a/README.md +++ b/README.md @@ -7,3 +7,29 @@ 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 + +## React Hooks + +- Function that performs a specific task inside React, like "hooking into" React state or component lifecycles +- Start with the word "use" (useState/ useEffect) +- Piece of code/function that someone wrote +- Hooks can be from React, or you can write them yourself (custom hooks), or you can import them from somewhere else (like a different framework like Redux) + +## useState hook + +- A hook that creates variables that React listens to. It "reacts" when the values of these variables change, by re-rendering the components! + +## useEffect hook + +- Use it to fetch data (async) +- Help control the amount of re-renders on the page +- Steps: + //Fetching data from API flow + //0. Install axios package + //1. Write an async function + //2. Make a request with axios + //3. Console.log what I'm getting back --- this is what we've already learned + //4. Import useEffect from 'react' --- this is new for React + //5. Call the async function inside useEffect + //6. Check my console.log and put the data in the state + //7. React renders something on the screen based on state diff --git a/package-lock.json b/package-lock.json index fcfdb30..8e7ff14 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,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", @@ -5413,6 +5414,29 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.5.tgz", + "integrity": "sha512-Ii012v05KEVuUoFWmMW/UQv9aRIc3ZwkWDcM+h5Il8izZCtRVpDUfwpoFf7eOtajT3QiGR4yDUx7lPqHJULgbg==", + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axios/node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/axobject-query": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-3.2.1.tgz", @@ -14612,6 +14636,11 @@ "node": ">= 0.10" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, "node_modules/psl": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", diff --git a/package.json b/package.json index d3a860f..906fb59 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/App.js b/src/App.js index 7ca67c2..df4c918 100644 --- a/src/App.js +++ b/src/App.js @@ -1,6 +1,8 @@ import "./App.css"; import Character from "./components/character"; import charactersData from "./charactersData"; +import Counter from "./components/counter"; +import { useState } from "react"; // import something from 'some place' // App.js is the entry point to the rest of your app @@ -10,6 +12,7 @@ function App() { return charactersData.map((character) => { return ( {/* If we want a Character component for each character in our data, we could repeat it like below: */} - + /> */} {/* ... 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 */} diff --git a/src/components/character.js b/src/components/character.js index 5288581..912f056 100644 --- a/src/components/character.js +++ b/src/components/character.js @@ -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 @@ -22,6 +23,7 @@ const Character = (props) => {

{props.quote}

{/* using a component within a component also works! */} +
); diff --git a/src/components/counter.js b/src/components/counter.js new file mode 100644 index 0000000..883d160 --- /dev/null +++ b/src/components/counter.js @@ -0,0 +1,34 @@ +import { useState, useEffect } from "react"; + +const Counter = () => { + // let currentCount = 0; + + const [count, setCount] = useState(0); + const [favorite, setFavorite] = useState(false); + + const favoriteClicked = () => { + setFavorite(!favorite); + }; + + return ( +
+

Likes: {count}

+ +
+ + ⭐️🐙 + {favorite ? "⭐️" : ""} +
+ ); +}; + +export default Counter; From 220288ac53a1408bcb0c368a600136c33785609c Mon Sep 17 00:00:00 2001 From: tessawiedmann Date: Wed, 17 Jan 2024 13:37:26 +0100 Subject: [PATCH 2/6] Refactored to characterlist component --- src/App.js | 48 ++------------------------------ src/components/character-list.js | 23 +++++++++++++++ 2 files changed, 25 insertions(+), 46 deletions(-) create mode 100644 src/components/character-list.js diff --git a/src/App.js b/src/App.js index df4c918..3eeac78 100644 --- a/src/App.js +++ b/src/App.js @@ -1,57 +1,13 @@ import "./App.css"; -import Character from "./components/character"; -import charactersData from "./charactersData"; -import Counter from "./components/counter"; -import { useState } from "react"; +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 ( - - ); - }); - }; - return (
- {/* If we want a Character component for each character in our data, we could repeat it like below: */} - {/* - - */} - {/* ... 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()} +
); } diff --git a/src/components/character-list.js b/src/components/character-list.js new file mode 100644 index 0000000..b5a8213 --- /dev/null +++ b/src/components/character-list.js @@ -0,0 +1,23 @@ +import Character from "./character"; +import charactersData from "../charactersData"; + +const CharacterList = () => { + const charactersComponents = () => { + return charactersData.map((character) => { + return ( + + ); + }); + }; + + return
{charactersComponents()}
; +}; + +export default CharacterList; From 86d9ba0c69a49588cde861dfa532fd0ed0b1b6bb Mon Sep 17 00:00:00 2001 From: tessawiedmann Date: Wed, 17 Jan 2024 15:46:01 +0100 Subject: [PATCH 3/6] Add useEffect --- src/components/character-list.js | 35 +++++++++++++++++++++++++++++--- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/components/character-list.js b/src/components/character-list.js index b5a8213..a373903 100644 --- a/src/components/character-list.js +++ b/src/components/character-list.js @@ -1,9 +1,31 @@ import Character from "./character"; -import charactersData from "../charactersData"; +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 charactersData.map((character) => { + return characters.map((character) => { return ( { }); }; - return
{charactersComponents()}
; + // 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..."}
; }; export default CharacterList; From eb71de219515d9be55f86144ad05827616d20567 Mon Sep 17 00:00:00 2001 From: tessawiedmann Date: Wed, 17 Jan 2024 15:46:07 +0100 Subject: [PATCH 4/6] More comments --- README.md | 54 +++++++++++++++++++++------------------ src/App.js | 1 + src/components/counter.js | 13 ++++++---- 3 files changed, 38 insertions(+), 30 deletions(-) diff --git a/README.md b/README.md index 3ac8756..67c5473 100644 --- a/README.md +++ b/README.md @@ -8,28 +8,32 @@ This project was created with [Create React App](https://github.com/facebook/cre - Run `npm install` to install all the dependencies listed in package.json - Run `npm run start` to start the local development server -## React Hooks - -- Function that performs a specific task inside React, like "hooking into" React state or component lifecycles -- Start with the word "use" (useState/ useEffect) -- Piece of code/function that someone wrote -- Hooks can be from React, or you can write them yourself (custom hooks), or you can import them from somewhere else (like a different framework like Redux) - -## useState hook - -- A hook that creates variables that React listens to. It "reacts" when the values of these variables change, by re-rendering the components! - -## useEffect hook - -- Use it to fetch data (async) -- Help control the amount of re-renders on the page -- Steps: - //Fetching data from API flow - //0. Install axios package - //1. Write an async function - //2. Make a request with axios - //3. Console.log what I'm getting back --- this is what we've already learned - //4. Import useEffect from 'react' --- this is new for React - //5. Call the async function inside useEffect - //6. Check my console.log and put the data in the state - //7. React renders something on the screen based on state +## 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 diff --git a/src/App.js b/src/App.js index 3eeac78..358e3b6 100644 --- a/src/App.js +++ b/src/App.js @@ -7,6 +7,7 @@ import CharacterList from "./components/character-list"; function App() { return (
+ {/* Now have a new CharactersList component, to keep our App component more clean and organized */}
); diff --git a/src/components/counter.js b/src/components/counter.js index 883d160..5d32a9c 100644 --- a/src/components/counter.js +++ b/src/components/counter.js @@ -1,10 +1,13 @@ -import { useState, useEffect } from "react"; +import { useState } from "react"; const Counter = () => { - // let currentCount = 0; - + // 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); @@ -15,7 +18,7 @@ const Counter = () => {

Likes: {count}

- ⭐️🐙 + {/* If favorite is true, show the star, otherwise, show nothing */} {favorite ? "⭐️" : ""} ); From 0e9a53811ad1e8abf1c0f5708c09588226c462a8 Mon Sep 17 00:00:00 2001 From: tessawiedmann Date: Wed, 17 Jan 2024 15:56:33 +0100 Subject: [PATCH 5/6] Add branch info to readme --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 67c5473..cf7bae9 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,15 @@ This project was created with [Create React App](https://github.com/facebook/cre - 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 + +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 From 5a02a7ca12f2a5ae79f76df92f7e8ab355e123cb Mon Sep 17 00:00:00 2001 From: tessawiedmann Date: Mon, 22 Jan 2024 14:24:41 +0100 Subject: [PATCH 6/6] Update readme --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index cf7bae9..b7738f2 100644 --- a/README.md +++ b/README.md @@ -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