Learning React: 5 Important Principles About Hooks You Have To Know
For Example, Sometimes You Don’t Need Hooks!

We are rapidly approaching 2025, and React will undoubtedly still be one of the most popular choices for creating front-end applications.
This is not because it has no disadvantages but because of the enormous community and massive popularity that React has gained over the years. And we can’t forget about React 19, which promises us a lot, so React will not lose its position.
It is hard to imagine React without hooks, but unfortunately, I often see developers overusing them. As a result, they have to solve problems with dependency arrays, useless re-renders and all the mess they have created because, profoundly in their minds, there’s a concern that everything can be done only with hooks in React.
In this article, I will discuss five principles every new React programmer should know to improve and simplify their code.
1. Not every function has to be a hook
Let’s start with the basics and check out the definition.
Hooks are defined using JavaScript functions. They represent a particular type of reusable UI logic and are restricted in where they can be called.
Hooks may look like functions, but there are some differences:
- Hooks can be used only in functional components or custom hooks.
- The hook’s name always starts with “use,” followed by a capital letter.
- If the custom hook doesn’t contain calls to other hooks inside, it is a function, not a hook. This is important because it helps identify if there is some state logic or effects inside.
Let’s create a simple custom hook called useBoolean to meet these requirements. We can use it to open/close panels, dialogues, show/hide elements, etc.
If you look at the official documentation, you will find that wrapping any functions that hook returns into useCallback is recommended, and we will do that.
interface ICallbacks {
setFalse: () => void;
setTrue: () => void;
toggle: () => void;
}
const useBoolean = (initialValue: boolean): [boolean, ICallbacks] => {
const [value, setValue] = useState(initialValue);
const setFalse = useCallback(() => {
setValue(false);
}, []);
const setTrue = useCallback(() => {
setValue(true);
}, []);
const toggle = useCallback(() => {
setValue(curValue => !curValue);
}, []);
return [value, {setFalse, setTrue, toggle}];
}
When we remember the basics, we can dive deeper and better understand some nuances.
2. Learn about re-renders
You probably ask why.
Understanding how React works and what happens when you change the component’s state through the setter function is essential. At first glance, it looks like you changed state, and the result must appear immediately, but is it?
When you know what is happening when the state is being changed, it is much easier to understand when and why useEffect or other hooks with dependency arrays are triggered.
Let’s look at a simple example. Imagine we press the button for the first time and call onChangeText, passing the value “newValue”.
const [text, setText] = useState("defaultValue");
const onChangeText = (value: string) => {
// value equals "newValue"
setText(value);
console.log(text); //What will be the value here?
}
It may look like we should see “newValue” in the console, but actually, there will be “defaultValue.” Why? Because the new value will be available only after re-rendering.
Here is necessary to see the steps that are happening:
- We change the state through the setter function, telling React to take action.
- Render. React calls your component to calculate a new JSX, which will be returned.
- Commit. After calculating changes, React will modify the DOM; minimal actions will be applied.
- After the previous steps, you will see the visual changes on the screen (“browser rendering”).
Every time you want to change the value in the state, you need to remember that these steps are done each time.
3. useState is not always the correct answer
In React, we have two ways to manage the component’s state — useState and useReducer. The second is less popular because it is meant for more complex objects in the state and honestly looks too tricky at first glance for new programmers, but it is not.
However, useState looks very simple and understandable, so new programmers often use it more than is required.
Depending on user interactions, it is intended to manage the state for redrawing components. If you want to remember something without rendering it, you probably shouldn’t put it in the state. useRef would be the best option.
You don‘t need useState if:
- You want to remember some values during re-renders without showing them to the user.
- You already have data in the state, or you receive it through props but need to transform it; you don’t need to keep that new value in the new useState object, create a new variable and operate with that without triggering useless re-renders.
You need to keep the value in a state if:
- You want to redraw the component when the value changes; the most popular examples are showing/hiding panels, spinners, error messages, and modifying arrays.
Simplify your code from this:
/**
This approach gives you useless re-renders and unnecessary
usage of useEffect.
When the name or description changes
and React re-renders the component,
React will check if there is functionality that depends on these values.
useEffect will be triggered when the name or description changes,
creating a new re-render.
**/
const [name, setName] = useState('name');
const { description, index } = props;
const [fullName, setFullName] = useState('');
useEffect(()=>{
setFullName(`${name} - ${description}`);
},[name, description]);
To this:
/**
we can use React default behaviour and get the correct value when the name
or descriptions are changed without triggering one more re-render.
**/
const [name, setName] = useState();
const { description, index } = props;
const nameWithDescription = `${name} - ${description}`;
4. You should be very careful with useEffect
We used specific lifecycle methods with Class components in particular cases. Since the React 16.8 version may look like we have one useEffect hook for everything.
Not for everything, but due to a lack of documentation and incredible hype around hooks in 2019, there was a tendency to use it in all possible places.
Let’s look at the official documentation:
useEffect
is a React Hook that lets you synchronise a component with an external system.
But in reality, we use useEffect much more than needed. It is excellent for fetching data when the component mounts, but unfortunately, new programmers tend to change the state using this hook, which is not the best solution.
Stop and review your code if you find yourself writing one useEffect after another inside of one component. You usually don’t need them, and you can quickly eliminate them.
You don’t need a useEffect if:
- You need to handle user events (clicks); if you know what actions trigger some logic, don’t use useEffect to call that logic.
- You need to transform data for rendering, for example, concatenating strings from state or props. Putting reactive values or logic into a dependency array may call useEffect too often, leading to infinity loops.
You need to choose useEffect if:
- you want to fetch data on component mounting, set intervals and use it for synchronising state with other systems
5. Don’t be afraid of useRef
Unfortunately, useRef is underrated. It is not among the most popular hooks, but it is beneficial. Knowing how and where to use it can achieve great results.
Let’s start with the basics.
useRef is a React Hook that lets you reference a value not needed for rendering. — from React official documentation.
React will remember the value you create through useRef, whether you are making a Javascript object referencing a node in DOM or a simple value, and it will not be lost during re-renders.
What does it give us?
- We can easily reach the elements in the DOM. For example, you can get the input field’s value, focus on specific elements, get their height and width, scroll to a particular part of the screen, and more.
- You can remember any data you need without re-rendering components. For example, if you need a counter or a timer, choose useRef but not useState.
Examples:
// reference to a number
const refCount= useRef(0);
//reference to an input element
const refInputField = useRef(null);
/**
To access the value, you need to use the current property.
useRef doesn't trigger re-renders so you can use them inside useEffect
without stressing about the dependency array
*/
const onClick = () => {
refCount.current = refCount.current + 1;
refInputField.current.focus();
}
return (
<>
<button onClick={onClick}>
Click me!
</button>
<input ref={refInputField}></input>
</>
);
Hooks are great, and you should use them. You can achieve a lot if you understand how React works and apply hooks correctly.
That’s all about tips for new React developers. I hope these tips will be helpful.