umm, so I’ve written this quite quickly, apologies if there are any minor errors and it might be slightly hard to grok at first so ask for clarification but:
Yes
No.
So you have a function, like
const Component = () => {
const [count, setCount] = React.useState(0);
return (
<button onClick={() => setCount(count => count + 1)}>{ count }</button>
);
};
count
is literally just a variable. So text in the button just renders the values of that variable.
When you click the button, that runs the setCount function. This causes a state change, so React runs the Compent
function again, but this time count
is 1. And if you click again, setCount gets ran, state changes, the Component
function runs again, with count
now 2.
For the component function to be called again, either the state needs to change or it needs to get new props. With your example, that isn’t going to happen:
const FetchComponent = () => {
async function getMovies() { /* async stuff */ }
return (
<button onClick={getMovies}>Get Movies</button>
);
};
There is no state change. You are console logging, and that will log eventually, but as I said, that isn’t any practical use. So say you use state:
const FetchComponent = () => {
const [movies, SetMovies] = React.useState([])
async function getMovies() {
/* async stuff */
setMovies(response);
}
return (
<button onClick={getMovies}>Get Movies</button>
);
};
The issue here is that getMovies
is asynchronous. setState
will happen straightaway, and at that point the response hasn’t come back from the API. So the FetchComponent
function will execute again, but this time the movies
variable will be undefined. Not what you want.
So this is where useEffect
comes in. It allows for side effects. Every useEffect
in the function runs every time the function runs. It basically lets you keep the useState
value in sync, keeps it correct according to what you want.
So ideally:
- You have some state that holds the list of movies
- You click a button, which makes a request
- wait for the response to return from the api, and only then update the state
- state change causes the component function to be executed again, this time with a list of moves as the state, which in turn executes the JSX functions that eventually render a list of movies to the DOM.
useEffect
is a function that takes two arguments: a function that it runs every time the overall component function is run, and an array of dependencies (I’ll cover that in a minute, it’s important).
The function itself shouldn’t be async, but you can put async function inside it (and execute it, you want the return value from it), which is what I’ll do here:
const FetchComponent = () => {
const [movies, SetMovies] = React.useState([]);
React.useEffect(() => {
async function getMovies() {
/* async stuff */
setMovies(response);
}
getMovies();
}, []);
return (
<button>Get Movies</button>
<p>{JSON.stringify(movies)}</p>
);
};
Note that I have no way now to trigger the getMovies
function. It will just run when the component first loads, populate the state with whatever comes back. useEffect
just keeps things in sync and runs functions you want to run: you can’t explicitly call anything inside it. I can access and modify any state values though.
So to manually make if run the getMovies
function, what I need to do is to force the component to rerender and execute the getMovies function. So lets add another state value, one that I will set when the button gets clicked. Now:
- You have some state that holds the list of movies (an array)
- You have some state that holds the search term you want to search for (a string)
- The search term starts as an empty string
- In useEffect, only run
getMovies
if the search string is not an empty string
- You click a button, which sets the search term
- The component function is called again, and useEffect runs
- Inside useEffect, the search string is now a nonempty string:
getMovies
runs.
getMovies
updates the movies state. Component function is executed again.
movies
is now a populated list of movies, go off and render to the DOM
const FetchComponent = () => {
const [movies, SetMovies] = React.useState([]);
const [searchString, setSearchString] = React.useState("");
React.useEffect(() => {
async function getMovies() {
/* async stuff */
setMovies(response);
}
if (searchString !== "") getMovies();
}, [searchString]);
return (
<button onClick={() => setSearchString("Avengers")}>Get Movies</button>
<p>{JSON.stringify(movies)}</p>
);
};
Finally, the array of dependencies, the second argument to useEffect
. I did say that useEffect runs every time the component function runs. Well, you can modify that behaviour by specifying values in the dependency array. If those values don’t change, then useEffect won’t run again. In this case it’s [serachString]
– useEffect should only run once when the component first loads, then only again if the search string changes.
The button sets the search string, the search string value changes, the search string is the dependency here, and so useEffect should run again.