React Hooks

useState

Handle a local state in the component
https://react.dev/reference/react/useState

The dispatch or setter function always has access to the previous value

const [light, setLight] = useState('red');

const handleChangeColor = (color: string) => {
	setLight((prev) => {
	    return color;
    });
};
setTodos[...todos, newTodo];

useEffect

Run side effects and cleanup when unmounting component.
https://react.dev/reference/react/useEffect

It is recommended to create "atomic effects", I mean, have several useEffects where each one has a single, well-defined responsibility. This makes the code much easier to read, debug and maintain.

// countDown effect
    useEffect(() => {
        if (countdown === 0) return;

        const intervalId = setInterval(() => {
            setCountdown(prev => prev - 1);
        }, 1000);

        return () => {
            clearInterval(intervalId);
        }

    }, [countdown]);

    // change traffic light color effect
    useEffect(() => {
        if (countdown === 0) {
            setCountdown(5);
            if (light === 'red') {
                setLight('green');
                return;
            }
            if (light === 'yellow') {
                setLight('red');
                return;
            }
            if (light === 'green') {
                setLight('yellow');
                return;
            }
        }
        return;
    }, [countdown, light]);

You cannot declare the callback function of a useEffect directly as async because the hook optionally expects a cleanup function or undefined . async and await always returns a promise.

 interface Pokemon {
    id: number;
    name: string;
    imageUrl: string;
}

interface Props {
    id: number;
}

 const getPokemonById = async (id: number) => {
        setIsLoading(true);

        const response = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
        const data = await response.json();

        setPokemon({
            id: id,
            name: data.name,
            imageUrl: `https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/${id}.png`,
        });

        setIsLoading(false);
    }

    useEffect(() => {
        getPokemonById(id);
    }, [id]);

useContext

The React hook useContext allows access to the current value of a context object created with createContext.

To provide a value to the context, you must use a Context (Context.Provider in legacy apps) component.

import { useContext, createContext } from "react";
const ThemeContext = createContext('light');

function ThemedButton() {
	const theme = useContext(ThemeContext);
	return (
		<button className={theme}>
			I am styled by theme context!
		</button>
	);
}

function App() {
	return(
		<ThemeContext value="dark">
			<ThemedButton />
		</ThemeContext>
	);
}

useRef

Changing the value of a function's useState causes a rerender of the component, while changing the current property of a reference created with useRef does not. That is, useRef holds data that should not cause a UI refresh.

import { useRef } from "react"

export const FocusScreen = () => {
    // Initial value null cause of react syncrony
    const inputRef = useRef<HTMLInputElement>(null);

    const handleClick = () => {
        console.log(inputRef.current?.value);
        inputRef.current?.select();
    }

    return (
        <div className="bg-gradient flex flex-col gap-4">
            <h1 className="text-2xl font-light text-white">Focus screen</h1>
            <input
                ref={inputRef}
                type="text"
                className="bg-white text-black px-4 py-2 rounded-md"
                autoFocus
            />
            <button
              className="bg-blue-500 text-white px-4 py-2 rounded-md cursor-pointer"
                onClick={handleClick}>
                Set Focus
            </button>
        </div>
    )
}

useId

useCallback

Note

Not neccesary since React v19. React Compiler automatically memoizes values and functions

The useCallback hook in React is used to memoize a callback function, ensuring that the same function reference is returned unless its dependencies change. This helps prevent unnecessary re-renders of child components that rely on the same function reference.

useMemo

Note

Not neccesary since React v19. React Compiler automatically memoizes values and functions

useMemo is a React Hook that lets you cache the result of a calculation between re-renders.

useReducer

As your component grows and multiple related states come into play useReducer let´s you manage state updates in a centralized and predictable way.

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:
      return state;
  }
}

const [state, dispatch] = useReducer(reducer, initialState);

return (
  <>
    <p>{state.count}</p>
    <button onClick={() => dispatch({ type: 'increment' })}>+</button>
    <button onClick={() => dispatch({ type: 'decrement' })}>-</button>
  </>
);
When to Use useReducer

useOptimistic

useOptimistic(state, updateFn)

useOptimistic is a new React Hook that lets you show a different state while an async action is underway.

const [comments, setComments] = useState<Comment[]>([
        { id: 1, text: 'Really awesomwe!' },
        { id: 2, text: 'I love it 🧡' },
    ]);

const handleAddComment = async (formdata: FormData) => {
    const messageText = formdata.get('post-message');
    
    addOptimisticComment(messageText);
    
	// emulates backend response
    await new Promise((resolve) => setTimeout(resolve, 3000));
    setComments((prev) => [
            ...prev,
            {
                id: new Date().getTime(),
                text: messageText,
            },
	    ]);
};

const [optimisticComments, addOptimisticComment] = useOptimistic(comments, (currentComments, newCommentText: string) => {
        return [...currentComments, {
            id: new Date().getTime(),
            text: newCommentText,
            optimistic: true,
        }]
});

return (
<>
	{/* map optimisticComments instead comments */}
	{/* ... */}
	{/* form */}
	<form
		action={event => handleAddComment(event)}
	    className="flex flex-col items-center justify-center bg-gray-300 w-[500px] rounded-b-3xl p-4"
>	
		<input
	        type="text"
	        name="post-message"
	        placeholder="Wite a comment"
	        required
		    className="w-full p-2 rounded-md mb-2 text-black bg-white"
	    />
		<button
	        type="submit"
	        disabled={false}
	        className="bg-blue-500 text-white p-2 rounded-md w-full">	
		    Send
		</button>
	</form>
</>
)
Note

The get() method of FormData interface returns the first value associated with a given key from within a FormData object. For example, in the previous code name="post-message" from an input type text
FormData: get method

useTransition

Allows rendering parts of the UI in the background. useTransition let you mark state updates as non-urgent transitions keeping the UI responsive during heavy renders.

In this scenario, typing updates the input immediately, while rendering the filtered list is marked as low priority.

import { useState, useTransition } from 'react';

function FilterableList({ items }) {
  const [query, setQuery] = useState('');
  const [filteredList, setFilteredList] = useState(items);
  const [isPending, startTransition] = useTransition();

  const handleSearch = (e) => {
    const value = e.target.value;
    
    // 1. Urgent update: Update input immediately
    setQuery(value);

    // 2. Non-urgent update: Defer heavy list recalculation/render
    startTransition(() => {
      setFilteredList(items.filter(item => item.includes(value)));
    });
  };

  return (
    <div>
      <input type="text" value={query} onChange={handleSearch} />
      
      {isPending ? (
        <p>Updating list...</p>
      ) : (
        <ul>
          {filteredList.map((item, index) => (
            <li key={index}>{item}</li>
          ))}
        </ul>
      )}
    </div>
  );
}

Another use case combined with useOptimistic hook:

const [isPending, startTransition] = useTransition();

const [comments, setComments] = useState<Comment[]>([
        { id: 1, text: 'Really awesomwe!' },
        { id: 2, text: 'I love it 🧡' },
    ]);

const handleAddComment = async (formdata: FormData) => {
    const messageText = formdata.get('post-message');
    
    addOptimisticComment(messageText);
    
	startTransition(async() => {
		await new Promise((resolve) => setTimeout(resolve, 3000));
	    setComments((prev) => [
	            ...prev,
	            {
	                id: new Date().getTime(),
	                text: messageText,
	            },
	    ]);
	});
};

const [optimisticComments, addOptimisticComment] = useOptimistic(comments, (currentComments, newCommentText: string) => {
        return [...currentComments, {
            id: new Date().getTime(),
            text: newCommentText,
            optimistic: true,
        }]
});

return (
<>
	{/* map optimisticComments instead comments */}
	{/* ... */}
	{/* form */}
	<form
		action={event => handleAddComment(event)}
	    className="flex flex-col items-center justify-center bg-gray-300 w-[500px] rounded-b-3xl p-4"
>	
		<input
	        type="text"
	        name="post-message"
	        placeholder="Wite a comment"
	        required
		    className="w-full p-2 rounded-md mb-2 text-black bg-white"
	    />
		<button
	        type="submit"
	        // replace previous false to wait for startTransition action
	        disabled={isPending}
	        className="bg-blue-500 disabled:bg-gray-400
	         text-white p-2 rounded-md w-full">	
		    Send
		</button>
	</form>
</>
)
Powered by Forestry.md