State and Events
State allows you to have the user interace with your application, update some data under the hood, and have that reflected in the page.
Events
Events means how you handle some user actions in your application. A full list of possible events can be found here. Essentially, you define functions that are called when certain actions are performed on elements in your page.
function App() {
const handleClick = () => {
console.log("button clicked");
};
return (
<div>
<button onClick={handleClick}>Click me</button>;
<button
onClick={() => {
console.log("Inline function click");
}}
>
Click me too!
</button>
</div>
);
}
useState
State tells your application to rerender the page when something changes in your application.
import { useState } from "react";
function App() {
const [count, setCount] = useState(0);
const handleClick = () => {
setCount(count + 1);
};
return <button onClick={handleClick} />;
}
useReducer
A Reducer is a way of combining multiple state variables into one object. This allows you to reduce the number of rerenders if multiple pieces of state are changing at the same time. useReducer
takes a reducer
function and an initial state. This reducer
function will run whenever we call dispatch
, and the argument to dispatch
will be passed as an argument action
along with the current state. The action
argument is usually an object which tells the reducer what type of update to do. Whatever is returned from this function will be the new state. The reducer
function cannot include async/await, promises, requests, etc. It is only for updating the state. You should not update the state directly unless you are using Immer, which is a library that automatically returns the new state for you.
import produce from "immer";
import { useReducer } from "react";
// Use constants for action to prevent typos
const INCREMENT_COUNT = "increment";
const SET_VALUE_TO_ADD = "change_value_to_add";
const DECREMENT_COUNT = "decrement";
const ADD_VALUE_TO_COUNT = "add_value_to_count";
const reducer = (state, action) => {
switch (action.type) {
case INCREMENT_COUNT:
state.count = state.count + 1;
return;
// If not using immer, you would need to do something like...
return {
...state,
count: state.count + 1,
};
case DECREMENT_COUNT:
state.count = state.count - 1;
return;
case ADD_VALUE_TO_COUNT:
state.count = state.count + state.valueToAdd;
state.valueToAdd = 0;
return;
case SET_VALUE_TO_ADD:
state.valueToAdd = action.payload;
return;
default:
return;
}
};
function CounterPage({ initialCount }) {
// wrapper reducer with produce to use immer
const [state, dispatch] = useReducer(produce(reducer), {
count: initialCount,
valueToAdd: 0,
});
const increment = () => {
dispatch({
type: INCREMENT_COUNT,
});
};
const decrement = () => {
dispatch({
type: DECREMENT_COUNT,
});
};
const handleChange = (event) => {
const value = parseInt(event.target.value) || 0;
dispatch({
type: SET_VALUE_TO_ADD,
payload: value,
});
};
const handleSubmit = (event) => {
event.preventDefault();
dispatch({
type: ADD_VALUE_TO_COUNT,
});
};
}
export default CounterPage;
Binding inputs
function Input({ onSubmit }) {
const [text, setText] = useState("");
const handleChange = (event) => {
setText(event.target.value);
};
const handleSubmit = (event) => {
event.preventDefault();
onSubmit(text);
setText("");
};
return (
<div>
<form onSubmit={handleSubmit}>
<input className="input" value={title} onChange={handleChange} />
<button className="button">Submit</button>
</form>
</div>
);
}
useEffect
useEffect is a function that runs when the application is initially rendered and sometimes when it is rerendered. The arrow function provided is always called on the initial render, and if a variable passed in the second argument is changed in the rerender, then the arrow function is called again. If no second argument is passed, then the function is called on every rerender.
import { useEffect, useState } from "react";
function Comp() {
useEffect(() => {
console.log("Run on initial render");
}, []);
useEffect(() => {
console.log("Run on every render");
});
const [text, setText] = useState("");
useEffect(() => {
console.log("Run when 'text' is changed");
}, [text]);
}
The only thing we can return from useEffect is a function, and this function gets called before the next time the useEffect function is run, so from the second render on.