October 17, 2024 - Edited on November 05, 2024
Tip #1: Simplify useState
Types
// instead of this const [userId, setuserId] = useState<string | undefined>(undefined); // do this const [userId, setuserId] = useState<string>();
Explanation: Whe you don't provide an initial state to useState
, it is undefined
by default.
Tip #2: Avoid Premature Components
React components are cool.
But creating components too early will make it harder to work with:
- you have to jump back and forth between and the component and it's parent
- you have to define the Props too early, often leading to an unsuitable API
Instead, consider writing inline components.
Once you see that there is repetition (or needing an item-level state) then extracting the code to a component is easy.
Before:
const TaskList = ({ tasks, toggleTask }) => ( <div> <h1>Task List</h1> <ul> {tasks.map((task) => ( <li key={task.id} onClick={() => toggleTask(task.id)} style={{ textDecoration: task.completed ? "line-through" : "none", cursor: "pointer", }} > {task.title} </li> ))} </ul> </div> );
After
const TaskList = ({ tasks, toggleTask }) => ( <div> <h1>Task List</h1> <ul> {tasks.map((task) => ( <TaskItem key={task.id} task={task} onToggle={() => toggleTask(task.id)} /> ))} </ul> </div> );
Tip #3: Fix React Context Boilerplate
React has some rough edges... one of them is React Context.
Creating a react context is too BOILERPLATY.
Checkout how much code you need just to share one value globaly:
// Without createReactContext import React, { createContext, useContext, useState, ReactNode } from "react"; // Step 1: Define the shape of the context interface TitleContextType { title: string; } // Step 2: Create the context with an initial undefined value const TitleContext = createContext<TitleContextType | undefined>(undefined); // Step 3: Define a provider component props interface TitleProviderProps { children: ReactNode; } // Step 4: Create a provider component props export const TitleProvider: React.FC<TitleProviderProps> = ({ children }) => { const [title] = useState("Default Title"); return ( <TitleContext.Provider value={{ title }}>{children}</TitleContext.Provider> ); };
But with this one function you can simplify it to look like this:
// 🔥 definition (just provide the business logic) const TitleContext = createReactContext(() => { const [title, setTitle] = useState(""); return { title, setTitle }; }); // ✅ Built-in Provider <TitleContext.Provider>...</TitleContext.Provider>; // ✅ Usage in Components TitleContext.use();
The createReactContext
function. (Copy and paste is in you code repository.)
function createReactContext<T>( init: () => T, defaultValue: T | undefined = undefined, ) { const Context = createContext<T | undefined>(defaultValue); function Provider({ children }: { children?: ReactNode }) { const value = init(); return <Context.Provider value={value}>{children}</Context.Provider>; } function useEasyContext() { const context = useContext(Context); if (context === undefined) { throw new Error( "Context must be used within a Provider! Wrap wour app in <Context.Provider/>", ); } return context; } return { use: useEasyContext, Provider }; }