The useState hook allows us to create state variables in a React function component.
import { useState } from 'react';
export default function App() {
const [ language ] = useState('Javascript')
return <h1>I am learning {language}</h1>;
}
import { useState } from 'react';
export default function App() {
const [ language, setLanguage ] = useState('Javascript')
function changeLanguage() {
setLanguage('React Hooks')
}
return <h1 onClick={changeLanguage}>I am learning {language}</h1>;
}
import { useState } from 'react';
export default function App() {
const [language, setLanguage] = useState('Javascript')
function changeLanguage() {
setLanguage('React Hooks')
}
return <h1 onClick={changeLanguage}>I am learning {language}</h1>;
}
import { useState } from "react";
export default function App() {
const [ language, setLanguage ] = useState("Javascript");
const [ years, setYears ] = useState(0);
function changeLanguage() {
setLanguage("React Hooks");
}
function addYear() {
setYears(prev => prev + 1)
}
return (
<div>
<h1 onClick={changeLanguage}>
I've learned {language} for {years} years
</h1>
<button onClick={addYear}>Add Year</button>
</div>
);
}
import { useState } from "react";
export default function App() {
const [ state, setState ] = useState({
language: "Javascript",
years: 0
})
function changeLanguage() {
setState({...state, language: "React Hooks"});
}
function addYear() {
setState(prev => {
return {
...prev,
years: prev.years + 1
}
})
}
return (
<div>
<h1 onClick={changeLanguage}>
I've learned {state.language} for {state.years} years
</h1>
<button onClick={addYear}>Add Year</button>
</div>
);
}
The useEffect hook lets us perform side effects in function components. Side effects are when we need to reach into the outside world. Such as fetching data from an API or working with the DOM. Side effects are actions that can change our component state in an unpredictable fashion (that have cause 'side effects'). The useEffect hook accepts a callback function (called the 'effect' function), which will by default run every time the component re-renders.
import { useEffect } from "react";
export default function App() {
useEffect(() => {
document.body.style.background = 'blue';
document.body.style.color = 'orange';
})
return (
<div>
<h1>React App</h1>
</div>
);
}
import { useEffect, useState } from "react";
export default function App() {
const [ color, setColor ] = useState('blue')
useEffect(() => {
document.body.style.background = color;
document.body.style.color = 'grey';
}, [color]);
function changeColor() {
setColor('gold')
}
return (
<div>
<h1>React App</h1>
<button onClick={changeColor}>Change color</button>
</div>
);
}
import { useState, useEffect } from "react";
export default function App() {
const [ color, setColor ] = useState('blue');
useEffect(() => {
document.body.style.background = color;
document.body.style.color = 'orange';
window.addEventListener('keydown', handleEnterButton)
return () => {
window.removeEventListener('keydown', handleEnterButton)
}
}, [color]);
function changeColor() {
setColor('gold')
}
function handleEnterButton(event) {
if (event.keyCode === 13) {
setColor('red')
}
}
return (
<div>
<h1>React App</h1>
<button onClick={changeColor}>Get color</button>
</div>
);
}
import { useState, useEffect } from "react";
export default function App() {
const [ color, setColor ] = useState('blue');
const [ user, setUser ] = useState(null);
useEffect(() => {
fetch('https://jsonplaceholder.typicode.com/users')
.then(res => res.json())
.then(data => setUser(data[0]))
}, [])
useEffect(() => {
document.body.style.background = color;
document.body.style.color = 'white';
window.addEventListener('keydown', handleEnterButton);
return () => {
window.removeEventListener('keydown', handleEnterButton)
}
}, [color]);
function changeColor() {
setColor('gold')
}
function handleEnterButton(event) {
if (event.keyCode === 13) {
setColor('red')
}
}
return (
<div>
<h1>React App</h1>
<button onClick={changeColor}>Get color</button>
<br />
<br />
Current user: <pre>{JSON.stringify(user, null, 2)}</pre>
</div>
);
}
Refs are a special attribute that are available on all React components. They allow us to create a reference to a given element / component when the component mounts. The useRef hook allows us to easily use React refs. They are helpful (as in the example below) when we want to directly interact with an element, such as to clear its value or focus it, as with an input.
import { useRef } from 'react';
export default function App() {
const inputRef = useRef(null);
function handleClearInput() {
inputRef.current.value = "";
inputRef.current.focus();
}
return (
<form>
<input type="text" ref={inputRef} />
<button type="button" onClick={handleClearInput}>
Clear Input
</button>
</form>
);
}
The useCallback hook is used for improving our component performance. Callback functions are the name of functions that are "called back" within a parent component. The most common usage is to have a parent component with a state variable, but you want to update that state from a child component. useCallback memoizes our callback functions, so they not recreated on every re-render. Using useCallback correctly can improve the performance of our app.
import { useState, useCallback, memo } from 'react';
export default function App() {
const [ skill, setSkill ] = useState("");
const [ skills, setSkills ] = useState(["HTML", "CSS", "JavaScript"]);
function handleChangeInput(event) {
setSkill(event.target.value);
}
function handleAddSkill() {
setSkills(skills.concat(skill));
}
const handleRemoveSkill = useCallback((skill) => {
setSkills(skills.filter((s) => s !== skill));
}, [skills])
return (
<>
<input onChange={handleChangeInput} />
<button onClick={handleAddSkill}>Add Skill</button>
<SkillList skills={skills} handleRemoveSkill={handleRemoveSkill} />
</>
);
}
const SkillList = memo(({ skills, handleRemoveSkill }) => {
console.log('re-rendered whenever parent state is updated!')
return (
<ul>
{skills.map((skill) => (
<li key={skill} onClick={() => handleRemoveSkill(skill)}>
{skill}
</li>
))}
</ul>
);
});
The useMemo hook is very similar to useCallback and is for improving performance, but instead of being for callbacks, it is for storing the results of expensive operations. useMemo allows us to memoize, or remember the result of expensive operations when they have already been made for certain inputs. Memoization means that if a calculation has been done before with a given input, there's no need to do it again, because we already have the stored result of that operation.
import { useState, useMemo } from 'react';
const skills = [ "HTML", "CSS", "JavaScript", 'C++', 'Elm' ];
export default function App() {
const [searchTerm, setSearchTerm] = useState("");
const searchResults = useMemo(() => {
return skills.filter(s => s.includes(searchTerm));
}, [searchTerm])
function handleSearchInput(event) {
setSearchTerm(event.target.value);
}
return (
<>
<h3>Search Results</h3>
<input onChange={handleSearchInput} />
<ul>
{searchResults.map((result, i) => (
<li key={i}>{result}</li>
))}
</ul>
</>
);
}
In React, we want to avoid the following problem of creating multiple props to pass data down two or more levels from a parent component. It is fine to pass props through multiple components, but it is redundant to pass props through components which do not need it. Context is helpful for passing props down multiple levels of child components from a parent component and sharing state across our app component tree. The useContext hook removes the unusual-looking render props pattern that was required in consuming React Context before.
import { createContext, useState, useContext} from 'react';
const UserContext = createContext()
export default function App() {
const [user] = useState({ name: "Joe Smith" });
return (
<UserContext.Provider value={user}>
<Main />
</UserContext.Provider>
);
}
const Main = () => (
<>
<Header />
<br />
<div>Main app content</div>
</>
);
const Header = () => {
const user = useContext(UserContext)
return <h1>Welcome, {user.name}!</h1>;
}
The useReducer hook is for state management, much like useState and relies upon a kind of function called a reducer. Reducers are simple, predictable (pure) functions that take a previous state object and an action object and return a new state object. useReducer can be used in many of the same ways that useState can, but is more helpful for managing state across multiple components that may involve different operations or "actions".
import { useReducer } from "react";
const initialState = { count: 0 };
function reducer(state, action) {
switch (action.type) {
case 'increment':
return {count: state.count + 1};
case 'decrement':
return {count: state.count - 1};
default:
throw new Error();
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, initialState);
return (
<>
Count: {state.count}
<button onClick={() => dispatch({ type: 'decrement'}) }>-</button>
<button onClick={() => dispatch({ type: 'increment'}) }>+</button>
</>
);
}
export default function App() {
return (
<>
<Counter />
</>
);
}
import { useState, useLayoutEffect } from "react";
export default function App() {
const [randomNumber, setRandomNumber] = useState(0)
const [effectLogs, setEffectLogs] = useState([])
useLayoutEffect(
() => {
setEffectLogs(prevEffectLogs => [...prevEffectLogs, 'effect function has been invoked'])
},
[randomNumber]
)
return (
<div>
<h1>{randomNumber}</h1>
<button
onClick={() => {
setRandomNumber(Math.random())
}}
>
Generate random number!
</button>
<div>
{effectLogs.map((effect, index) => (
<div key={index}>{'🍔'.repeat(index) + effect}</div>
))}
</div>
</div>
)
}
A custom React hook is a function that runs inside of a component. It can run other hooks or other functions inside it. These functions/hooks can be recursive too. It makes patterns like render props and higher-order components unnecessary.
// counter-hook.js
import { useState } from "react";
function useCounter(val, step) {
const [count, setCount] = useState(val);
function Increment() {
setCount(count + step); }
function Decrement() {
setCount(count - step); }
return [count, Increment, Decrement];}
export default useCounter;
// App.js
import React from "react";
import useCounter from "./counter-hook";
function App() {
const [count, Increment, Decrement] = useCounter(0, 1);
return (
<div className="App">
<h1>{count}</h1>
<button onClick={Increment}>Increment</button>
<button onClick={Decrement}>Decrement</button>
</div>
);
}
export default App;
import { useState, useEffect } from 'react';
export const useLocalStorage = (key, defaultValue) => {
const storedValue = JSON.parse(localStorage.getItem(key));
const [value, setValue] = useState(storedValue || defaultValue);
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [value, key]);
return [value, setValue];
}
function App() {
const [items, setItems] = useLocalStorage('items', []);
const removeItem = (itemToBeDeleted) => {
setItems(items.filter((item) => itemToBeDeleted !== item));
};
return (
<div className="App">
<header className="App-header">
To Do items
<ItemList items={items} removeItem={removeItem} />
<AddItemForm addItem={addItem} />
</header>
</div>
);
}