Not so long ago I started working with functional components instead of class based ones. The main goal was to learn how to implement React Hooks inside them. This way we can write less code and make it more reusable.
The benefits of using hooks and functional components are reusability, simpler & shorter code and the simplicity of testing those components.
The usual class approach of things is now a thing of the past. And here I will share short and understandable react hooks cheat sheet. This is not a tutorial for hooks as there are many articles online and the docs are really good. This serves as a quick reference for people already somewhat familiar with writing hooks. If you are new to hooks, you can still take a look. With that said, let's begin.
UseState - similar to React state and setState
- with primitive value
const App = () => {
const [carSpeed, updateCarSpeed] = useState(10);
return (
<div>
<p>Car is going {carSpeed} km/h</p>
<button onClick={() => updateCarSpeed(carSpeed + 5)}>
Speed up
</button>
</div>
);
};
- with object
export const App = () => {
const [carForm, updateForm] = useState({});
const updateField = (e) => {
updateForm({ ...carForm, [e.target.name]: e.target.value });
};
const handleSubmit = (e) => {
e.preventDefault();
console.log(carForm);
};
return (
<form onSubmit={handleSubmit}>
<label>
Car Owner:
<input
value={carForm.owner}
name="owner"
onChange={updateField}
/>
</label>
<br />
<label>
Car model:
<input
value={carForm.model}
name="model"
onChange={updateField}
/>
</label>
<button>Submit</button>
</form>
);
};
UseEffect - similar to componentDidUpdate
- only triggers once (because of empty array param)
export const App = () => {
const [carsData, updateCars] = useState({});
useEffect(() => {
fetch("http://example.com/cars.json")
.then((resp) => resp.json())
.then((data) => {
updateCars(data);
});
}, []);
const renderCars = () => {
return carsData.cars.map((car) => {
<p key={car.id}>{car.name}</p>;
});
};
return <div>{renderCars()}</div>;
};
- trigger on carName variable change
export const App = () => {
const [carName, updateCarName] = useState("");
useEffect(() => {
console.log("changed");
}, [carName]);
return (
<div>
<input
value={carName}
onChange={(e) => updateCarName(e.target.value)}
/>
</div>
);
};
UseReducer with React.memo HOC and useCallback
- This example makes use of useReducer hook which acts similar to Redux. It has a reducer and actions that change the state in the reducer. We also make use of the React.memo and useCallback for the sole reason of not re-rendering new "Car" components when each car is checked that it is sold.
- UseCallback - this hook is used when you have a component with a frequently re-rendering child and to which you pass a callback to. Without it the addCar function would be re-instantiated each time a new car is added to the list.
// initial cars state
const initialState = [
{
id: id(),
name: "Audi A4",
description: 'Black tint with red wheels, 100kw',
sold: false
},
{
id: id(),
name: "Porsche 911",
description: 'Cherry red tint with dark golden wheels, 300kw',
sold: false
},
{
id: id(),
name: "Lamborghini Gallardo",
description: 'Lamborghini green with black wheels, 500kw',
sold: false
},
];
// action names
const CAR_ADD = 'CAR_ADD';
const CAR_SELL = 'CAR_SELL';
// the reducer
const reducer = (state, action) => {
if (action.type === CAR_ADD) {
return [action.payload, ...state];
}
if (action.type === CAR_SELL) {
return state.map(car => {
if (car.id !== action.payload.id) {
return car;
}
return { ...car, sold: !car.sold };
});
}
return state;
};
const App = () => {
const [cars, dispatch] = useReducer(reducer, initialState);
const addCar = useCallback(({ name, description }) => {
dispatch(
{
type: CAR_ADD,
payload: {
name,
description,
sold: false,
id: id()
}
},
[dispatch]
);
});
const toggleSold = useCallback(
id => {
dispatch({
type: CAR_SELL,
payload: {
id
}
});
},
[dispatch]
);
return (
<div style={{ maxWidth: 400, margin: '0 auto' }}>
<NewCarForm onSubmit={addCar} />
<Cars cars={cars} onSell={toggleSold} />
</div>
);
};
const Cars = ({ cars = [], onSell }) => {
return (
<div>
<h2>Cars ({cars.length})</h2>
{cars.map(car => (
<Car key={car.id} car={car} onSell={onSell} />
))}
</div>
);
};
const Car = React.memo(({ car, onSell }) => {
return (
<div style={{border:"1px solid", margin: 10, padding: 10}}>
<h3>{car.name}</h3>
<p>{car.description}</p>
<div>
<label>
<input
type="checkbox"
checked={car.sold}
onChange={() => onSell(car.id)}
/>
Sold
</label>
</div>
</div>
);
});
const NewCarForm = React.memo(({ onSubmit }) => {
const [name, setCarName] = useState('');
const [description, setCarDescription] = useState('');
const handleChange = e => {
e.preventDefault();
onSubmit({ name, description });
};
return (
<form onSubmit={handleChange}>
<input
placeholder="Car name"
type="text"
value={name}
onChange={event => setCarName(event.target.value)}
/>
<input
placeholder="Car description"
type="text"
value={description}
onChange={event => setCarDescription(event.target.value)}
/>
<input type="submit" />
</form>
);
});
That would be all, thank you for reading kind stranger. Do you have something of your own to add to the list? Let me know.
Top comments (9)
Every time the next state is based on the existing one, better use the callback version of the state updater. Here it would be
updateCarSpeed(prevCarSpeed => prevCarSpeed + 5)
.I will be glad to use the callback version. Thank you for the advice. Do you mind explaining in more detail, why exactly is it better to use the callback version?
I think it's similar to setState in the class components - you can be sure that you're updating the correct value, am I right @guico33 ?
Yes
If you're interested to see more functional components with TypeScript, check out our recent github.com/UnlyEd/next-right-now OSS release. We also prefer functional components over classes, and thus hooks over HOC.
Great cheat sheet!
Also there is a typo in the second object example.
I guess you wanted to call updateForm instead of updateField in the updateField function
Oh thank you!
Nice observation, I indeed did wanted to call updateForm. Fix coming right up :)
Nice article Juraj :)
Have my gratitude :)