We are going to learn another hook from React. Where useState() we learnt yesterday help us control the re-render of the UIs , the next hook - useEffect() help us to control coordinate "side effects" with the state of the component during it life cycle.
The useEffect Hook lets you perform side effects in function components
So, what is the side effect in a function?
A side effect is any execution that affects something outside the scope of the function being executed. It is not a React specific term, it's a general concept about the behavior of a function.
Some examples of side effects in React components are:
For example, this component sets the document title after React updates the DOM:
import React, { useState, useEffect } from "react";
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
// Update the document title using the browser API
document.title = `You clicked ${count} times`;
});
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>Click me</button>
</div>
);
}
So, when you call useEffect, you're telling React that your component needs to do something after render. React will remember the function you passed (we'll refer to it as our "effect"), and call it later after performing the DOM updates.
Why is useEffect called inside a component? Effects are declared inside the component so they have access to its props and state (count
state variable)
Does useEffect run after every render? By default, React runs the effects after every render — including the first render.
useEffect()
hook is a special function that accepts 2 arguments:
useEffect(callback,[dependencies]);
callback
is the function containing the side-effect logic. This callback function is executed right after changes were being pushed to DOM.dependencies
is an optional array of dependencies. useEffect()
executes callback only if the dependencies have changed between renderings.Let's check out one more example to really understand the order when we use useEffect
function App() {
let [userCount, setUserCount] = useState(0);
useEffect(() => {
alert("Component updated")
});
const handleOnClick = ()=>{
setUserCount(userCount + 1)
}
return (
<div>
<p>User Count: {userCount}</p>
<button onClick={handleOnClick}>
Add employee
</button>
</div>
);
}
In the code above, at the first render, we see that the alert message will appear after the content render on the page. After that, whenever the user clicks the button, the state variable, userCount
will be incremented by one. As soon as the state variable updates, the react component will be re-rendered. After each re-rendering, useEffect
will be re-invoked, which lead to the relevant DOM element (the p
tag in this case) is updated and we get an alert notifying us that the component has been updated
The callback we're passing to useEffect
is called after every render of our component (including re-renders). In some cases, applying the effect after every render might create a performance problem.
Example: Now, let's build a component that will render to the DOM an input
and a div
that render the number of changes that we have made in the input.
import { useEffect, useState } from 'react';
function App() {
const [value, setValue] = useState("");
const [count, setCount] = useState(-1);
useEffect(() => {
setCount(count + 1);
});
const handleChange = (event) => {
setValue(event.target.value);
};
return (
<div>
<input type="text" value={value} onChange={handleChange} />
<div>Number of changes: {count}</div>
</div>
);
}
Try the code above and see what happen? . . .
Ok, with the code above, when the component re-renders due to user typing into the input, the useEffect(() => setCount(count + 1))
updates the count
state.
Remember that: When the state change, our Component will be rerender. And whenever the Component render, the useEffect will run
setCount
run that change the state, also log to the console hey
count
has been changed. So it re-render the component, thus continue our infinite loop.How to fix this? Simply put a dependencies array in as a second argument.
useEffect(() => setCount(count + 1),[value])
This will stop the loop!! Now, let have a look closer to dependencies array.
There are 3 cases we can see with the dependencies array. When dependencies are:
import { useEffect, useState } from 'react';
function App() {
const [value, setValue] = useState('');
const [count, setCount] = useState(-1);
useEffect(() => {
setCount(count + 1)
},[]);
const handleChange = (event) => {
setValue(event.target.value);
}
return (
<div>
<input type="text" value={value} onChange={handleChange} />
<div>Number of changes: {count}</div>
</div>
)
}
import { useEffect, useState } from 'react';
function App() {
const [value, setValue] = useState('');
const [count, setCount] = useState(-1);
useEffect(() => {
setCount(count + 1)
},[value]);
const handleChange = (event) => {
setValue(event.target.value);
}
return (
<div>
<input type="text" value={value} onChange={handleChange} />
<div>Number of changes: {count}</div>
</div>
)
}
module export App
We have an API endpoint to fetch data that is
let url = "https://newsapi.org/v2/everything";
// and some enpoints
/**
https://newsapi.org/v2/everything?q="tea"
https://newsapi.org/v2/everything?q="milk"
https://newsapi.org/v2/everything?q="coffee"
*/
category
state changeHere is the first 6 steps:
import React, { useState, useEffect } from "react";
const API_KEY="97a8df3625884a2da87308f934ecfbbd" //get your own key from newsapi.org
const App = () => {
const [query, setQuery] = useState("");
const [title, setTitle] = useState("")
const getData = async () => {
let url = "https://newsapi.org/v2/everything";
url += `?q=${query}&apiKey=${API_KEY}`;
console.log(query)
const res = await fetch(url);
const data = await res.json()
console.log("all news: ", data);
};
const handleQuery = (event) => {
setQuery(event.target.value);
};
return (
<>
<h1>{title}</h1>
<h2>{query}</h2>
<button id="btn1" onClick={handleQuery} value="tea">
Tea
</button>
<button id="btn2" onClick={handleQuery} value="milk">
Milk
</button>
<button id="btn3" onClick={handleQuery} value="coffee">
Coffee
</button>
</>
);
};
export default App;
If we run the code above, we would see three button. If we click any of the button we would see the h2
change the value accordingly. We haven't make any API calls which would have result in the getData
function executed thus the console.log with I will get data from the url
.
So now, we implement step 6: try to execute the getData
function after change the query
state variable with useEffect()
import React, { useState, useEffect } from "react";
const API_KEY="97a8df3625884a2da87308f934ecfbbd" //get your own key from newsapi.org
const App = () => {
const [query, setQuery] = useState("");
const [title, setTitle] = useState("")
useEffect(()=>{
const getData = async () => {
let url = "https://newsapi.org/v2/everything";
url += `?q=${query}&apiKey=${API_KEY}`;
const res = await fetch(url);
const data = await res.json()
console.log("all news: ", data);
setTitle(data.articles[0].title)
};
getData()
},[query])
const handleQuery = (event) => {
setQuery(event.target.value);
};
return (
<>
<h1>{title}</h1>
<h2>{query}</h2>
<button id="btn1" onClick={handleQuery} value="tea">
Tea
</button>
<button id="btn2" onClick={handleQuery} value="milk">
Milk
</button>
<button id="btn3" onClick={handleQuery} value="coffee">
Coffee
</button>
</>
);
};
export default App;
First, let's test it without the weird array above, what would you see? It works, right? We're able to solve the first problema about the "late" update. But look at your console, it still run the getData
function when we click to the same button. So it works but we need to optimized the code. That's why we need that weird array which called dependencies array
The following read is pretty cool regarding our today topics.
An old enemy, but this time, we come prepared and equiped with React Js.
mkdir react-coder-news
cd react-coder-news
npx create-react-app .
Pray to React god untill we see happy hacking
npm install react-bootstrap
index.html
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css"
integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC"
crossorigin="anonymous"
/>
We have the file structure:
|-node_modules/
|-public/
|-index.html
|-src
|-App.js
|-index.js
|-App.css
.env
.gitignore
pakage-lock.json
package.json
README.md
It is important to understand the structure of our data before start coding. The benefits of using an external API like NewsApi are well documented, tested and ready to use.
The strategy here is to map the required features with the provided API endpoints before we start coding. So that we would have a clear vision of what would be our main focus.
Features | Endpoint |
User see 20 news | https://newsapi.org/v2/everything&apiKey=key |
User filter with Category | https://newsapi.org/v2/top-headlines?category=&country=us&apiKey=key |
User can search (basic) | https://newsapi.org/v2/everything?q=category>&apiKey=key |
User can use Pagination | https://newsapi.org/v2/everything?page=page &apiKey=*key |
As we can see after list out all the endpoints that would be used, we could also list out the list of variables that will be change according to use cases.
They are :
q
= for category requestcategory
= for category filtered requestapiKey
= for authorization to access the APIpage
= for pagination requestInternet is a dangerous place. API keys eventhough free, sometime contain info that should not be leak. For examples, we probaly dont want me to use up all our API-call-allowance before our demo day, right?. Would not be fun at all not be able to call any data in our final project. So how will an API key leak? If we put it in our code, it is visible to the internet.
It is recommended to keep all our keys
and password
in .env
file. Then also use .gitignore
to prevent pushing online to github. So let do that. In our .env
, add
REACT_APP_API_KEY=ourKey
REACT_APP_
.env
file, we need to stop the terminal then npm start
again to use the variable inside .env
we useconst apiKey = process.env.REACT_APP_API_KEY;
//test
console.log(apiKey);
Again, before starting to code. We should identify what would be our file structure. We will think about our components tree. We assume that we could create any components (we hope) perfectly. Then with that assumption, we design the component tree so that we could distinguish which component do what task. Which is statefull? which is stateless? Where should we trigger fetch data? ...
With all the hints above so let's now build the app.
Given this suggested components tree
src/
|- App.js
|- index.js
|- Components/
|- SideMenu.js
|- MainPage.js
|- Pagination.js
|- SearchBox.js
Now, form what we have learnt it's your turn to build up a news page.