Top 30 React Tips Every Developer Should Know

Top 30 React Tips Every Developer Should Know

Introduction?

Whether you work as a software developer or build something from scratch as a side hustle, you will always require smart ways to code effectively with React. You may be someone just starting or a seasoned professional, learning React tips to avoid common mistakes is a no-brainer.?

While working with React, you might discover several issues along the way. This is why best practices and useful tips around React always act as a guiding star whenever you feel stuck resolving daily issues in React.?

From a personal viewpoint, I have mentioned the best 30 React tips that have helped React developers in my company, and will also help you. The last 5 tips will surely make you think of this guide as something you would have given yourself at the beginning of your career.

1. Use useCallback to Memoize Functions

When a parent component passes a function as a prop to a child, React re-creates the function reference on every render, even if the functions logic hasnt changed. This can trigger unnecessary re-renders. useCallback memoizes the function, ensuring it only changes when its dependencies change.

Common Mistake:

Here, handleClick is re-created on every render of the Parent component, causing the Child component to re-render unnecessarily.

jsx

function Parent() {

const handleClick = () => {

    console.log('Button clicked');

  };

  return <Child onClick={handleClick} />;

}

function Child({ onClick }) {

  return <button onClick={onClick}>Click Me</button>;

}        

??

Correct Approach:

With useCallback, the handleClick function reference remains stable, avoiding unnecessary re-renders of Child.

jsx

function Parent() {

const handleClick = useCallback(() => {

    console.log('Button clicked');

  }, []);

  return <Child onClick={handleClick} />;

}

function Child({ onClick }) {

  return <button onClick={onClick}>Click Me</button>;

}        

??

2. Avoid Inline Functions in JSX

Defining functions inline within JSX leads to a new function being created on every render. This can cause unnecessary re-renders of child components or negatively impact performance when such functions are passed as props.

Common Mistake:

In this example, the inline arrow function inside the onClick prop is recreated on every render, reducing performance and making the code harder to test.

jsx

function Parent() {

return (

    <button onClick={() => console.log('Button clicked')}>

      Click Me

    </button>

  );

}        

Correct Approach:

By defining the handleClick function outside of the JSX, it is not recreated on every render, improving performance and clarity.

jsx

function Parent() {

const handleClick = () => {

    console.log('Button clicked');

  };

  return <button onClick={handleClick}>Click Me</button>;

}        

3. Use TypeScript for Type Safety

TypeScript enforces type checking during development, reducing runtime errors and improving code readability. It helps to define clear data structures and function signatures.

Common Mistake:

Here, there is no indication of what data types name and age should be. This can lead to runtime errors if the wrong types are passed.

jsx

function User({ name, age }) {

  return <p>{name} is {age} years old</p>;

}        

Correct Approach:

Using TypeScript, the User component now has clear expectations for its props, reducing potential bugs.

tsx

interface UserProps {

  name: string;

  age: number;

}

const User: React.FC<UserProps> = ({ name, age }) => {

  return <p>{name} is {age} years old</p>;

};        

4. Defer Rendering with Suspense and Lazy

Reacts Suspense and lazy let you defer loading components until they are needed, improving initial load times and performance.

Common Mistake:

In this example, both Dashboard and Profile components are loaded immediately, even if the user doesn't navigate to their routes.

jsx

import Dashboard from './Dashboard';

import Profile from './Profile';

function App() {

  return (

    <div>

      <Dashboard />

      <Profile />

    </div>

  );

}        

Correct Approach:

With Suspense and lazy, components are loaded only when required, improving performance.

jsx

import { lazy, Suspense } from 'react';

const Dashboard = lazy(() => import('./Dashboard'));

const Profile = lazy(() => import('./Profile'));

function App() {

  return (

    <Suspense fallback={<p>Loading...</p>}>

      <Dashboard />

      <Profile />

    </Suspense>

  );

}        

5. Avoid Complex State Logic in useEffect

Placing complex logic inside useEffect can make your code harder to debug and lead to unintended behavior. Instead, isolate the logic into separate functions or use custom hooks.

Common Mistake:

This approach makes the useEffect unnecessarily cluttered and harder to test.

jsx

function App() {

  useEffect(() => {

    const fetchData = async () => {

      const response = await fetch('/api/data');

      const data = await response.json();

      console.log(data);

    };

    fetchData();

  }, []);

}        

Correct Approach:

Isolating the logic into a separate function improves readability and reusability.

jsx

function fetchData() {

  return fetch('/api/data').then((response) => response.json());

}

function App() {

  useEffect(() => {

    fetchData().then((data) => console.log(data));

  }, []);

}        

6. Use useLayoutEffect for Synchronous Side Effects

useLayoutEffect is similar to useEffect but runs synchronously after all DOM mutations. Use it when you need to perform measurements or updates to the DOM before it is painted.

Common Mistake:

This might log incorrect measurements as useEffect runs after the DOM is painted.

jsx

function Component() {

  useEffect(() => {

    const width = document.getElementById('box').offsetWidth;

    console.log(width);

  });

  return <div id="box" style={{ width: '100px' }}></div>;

}        

Correct Approach:

Using useLayoutEffect ensures the measurement happens before the DOM is painted.

jsx

function Component() {

  useLayoutEffect(() => {

    const width = document.getElementById('box').offsetWidth;

    console.log(width);

  });

  return <div id="box" style={{ width: '100px' }}></div>;

}        

7. Use Error Boundaries to Catch Runtime Errors

Error boundaries help to catch runtime errors in React components and display fallback UI, preventing the entire app from crashing.

Common Mistake:

If ComponentThatMightThrowError throws an error, the app crashes.

jsx

function App() {

  return <ComponentThatMightThrowError />;

}        

Correct Approach:

Using an error boundary ensures graceful error handling.

jsx

class ErrorBoundary extends React.Component {

  constructor(props) {

    super(props);

    this.state = { hasError: false };

  }

  static getDerivedStateFromError() {

    return { hasError: true };

  }

  render() {

    if (this.state.hasError) {

      return <h1>Something went wrong.</h1>;

    }

    return this.props.children;

  }

}

function App() {

  return (

    <ErrorBoundary>

      <ComponentThatMightThrowError />

    </ErrorBoundary>

  );

}        

8. Avoid Using Array Index as key in Lists

Using the array index as the key prop can lead to rendering issues when items are added, removed, or reordered in the list.

Common Mistake:

Here, using the index as the key can cause issues when the list changes.

jsx

const items = ['Apple', 'Banana', 'Cherry'];

function List() {

  return (

    <ul>

      {items.map((item, index) => (

        <li key={index}>{item}</li>

      ))}

    </ul>

  );

}        

Correct Approach:

Using a unique identifier like id ensures proper rendering and key stability.

jsx

const items = [{ id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }, { id: 3, name: 'Cherry' }];

function List() {

  return (

    <ul>

      {items.map((item) => (

        <li key={item.id}>{item.name}</li>

      ))}

    </ul>

  );

}        

9. Prevent Re-Renders with shouldComponentUpdate

In class components, React re-renders the component whenever props or state are updated. To prevent unnecessary renders, you can implement the shouldComponentUpdate lifecycle method to compare the new props or state with the old ones and decide whether a re-render is necessary.

Common Mistake:

Here, the render method will be called even if the value prop does not change, causing unnecessary re-renders.

jsx

class MyComponent extends React.Component {

  render() {

    console.log("Rendered!");

    return <div>{this.props.value}</div>;

  }

}

function App() {

  return <MyComponent value="Hello" />;

}        

Correct Approach:

By implementing shouldComponentUpdate, the component only re-renders if the value prop changes.

jsx

class MyComponent extends React.Component {

  shouldComponentUpdate(nextProps) {

    return nextProps.value !== this.props.value;

  }

  render() {

    console.log("Rendered!");

    return <div>{this.props.value}</div>;

  }

}

function App() {

  return <MyComponent value="Hello" />;

}        

10. Minimize Re-Renders Using React.memo

React.memo is a higher-order component that prevents functional components from re-rendering if their props remain unchanged. This is especially useful for performance optimization in functional components.

Common Mistake:

Here, the Child component will re-render even if the value prop does not change, wasting resources.

jsx

function Child({ value }) {

  console.log("Child rendered");

  return <div>{value}</div>;

}

function Parent() {

  return <Child value="Hello" />;

}        

Correct Approach:

By wrapping the Child component with React.memo, it avoids re-renders unless the value prop changes.

jsx

const Child = React.memo(({ value }) => {

  console.log("Child rendered");

  return <div>{value}</div>;

});

function Parent() {

  return <Child value="Hello" />;

}        

11. Avoid Recreating Objects/Arrays Inside render

Creating new objects or arrays inside the render method or a functional components body causes React to treat them as new references on every render, triggering unnecessary re-renders.

Common Mistake:

Here, the items array is re-created on every render, causing the Child component to re-render unnecessarily.

jsx

function App() {

  return <Child items={[1, 2, 3]} />;

}

function Child({ items }) {

  console.log("Child rendered");

  return <ul>{items.map((item) => <li key={item}>{item}</li>)}</ul>;

}        

Correct Approach:

By defining the items array outside the component, React recognizes it as a stable reference, avoiding unnecessary re-renders.

jsx

const items = [1, 2, 3];

function App() {

  return <Child items={items} />;

}

function Child({ items }) {

  console.log("Child rendered");

  return <ul>{items.map((item) => <li key={item}>{item}</li>)}</ul>;

}        

12. Use useReducer for Complex State Logic

When managing complex state with multiple actions or interdependent state variables, useReducer provides a more structured approach compared to useState. It separates the state logic into a reducer function, improving code clarity.

Common Mistake:

Here, the state logic for count and step is scattered, making it harder to maintain as the logic grows.

jsx

function Counter() {

  const [count, setCount] = useState(0);

  const [step, setStep] = useState(1);

  const increment = () => setCount(count + step);

  return (

    <div>

      <button onClick={increment}>Increment</button>

      <p>Count: {count}</p>

    </div>

  );

}        

Correct Approach:

With useReducer, the state logic is centralized, making it easier to manage and extend.

jsx

function reducer(state, action) {

  switch (action.type) {

    case "increment":

      return { ...state, count: state.count + state.step };

    case "setStep":

      return { ...state, step: action.step };

    default:

      throw new Error("Unknown action");

  }

}

function Counter() {

  const [state, dispatch] = useReducer(reducer, { count: 0, step: 1 });

  return (

    <div>

      <button onClick={() => dispatch({ type: "increment" })}>Increment</button>

      <input

        type="number"

        value={state.step}

        onChange={(e) => dispatch({ type: "setStep", step: Number(e.target.value) })}

      />

      <p>Count: {state.count}</p>

    </div>

  );

}        

13. Use forwardRef for Passing Refs to Child Components

When a parent needs access to a DOM element inside a child component, you can use React.forwardRef to forward the ref to the child’s DOM node.

Common Mistake:

Here, ref cannot be directly passed to the Input component because it's a functional component.

jsx

function Input(props) {

  return <input {...props} />;

}

function App() {

  const inputRef = useRef();

  return <Input ref={inputRef} />;

}        

Correct Approach:

Using React.forwardRef, the ref is forwarded to the input DOM element, making it accessible in the parent.

jsx

const Input = React.forwardRef((props, ref) => <input ref={ref} {...props} />);

function App() {

  const inputRef = useRef();

  return <Input ref={inputRef} />;

}        

14. Use React.StrictMode to Detect Potential Problems

React.StrictMode helps identify unsafe lifecycle methods, deprecated features, and side effects during development. It doesnt render anything in the UI but enables additional checks in development mode.

Common Mistake:

This approach works but doesn’t provide any additional debugging tools for detecting issues.

jsx

function App() {

  return <Component />;

}        

Correct Approach:

Wrapping the app in React.StrictMode enables React to detect potential problems during development.

jsx

import React from "react";

function App() {

  return (

    <React.StrictMode>

      <Component />

    </React.StrictMode>

  );

}        

15. Avoid Prop Drilling with Context API

Prop drilling occurs when props are passed down multiple levels of components, even if intermediate components don’t need the data. Reacts Context API allows you to avoid this by providing a way to share values across the component tree.

Common Mistake:

Here, theme is unnecessarily passed through Parent even though only Child needs it.

jsx

function App() {

  const theme = "dark";

  return <Parent theme={theme} />;

}

function Parent({ theme }) {

  return <Child theme={theme} />;

}

function Child({ theme }) {

  return <div>{theme}</div>;

}        

Correct Approach:

With Context API, theme is directly accessible in Child, avoiding unnecessary prop drilling.

jsx

const ThemeContext = React.createContext();

function App() {

  return (

    <ThemeContext.Provider value="dark">

      <Parent />

    </ThemeContext.Provider>

  );

}

function Parent() {

  return <Child />;

}

function Child() {

  const theme = useContext(ThemeContext);

  return <div>{theme}</div>;

}        

16. Keep Components Separate to Avoid Unnecessary Re-renders

Imagine you keep rebuilding your house every time someone enters the door, wasteful, right? Declaring components inside others is like rebuilding them on every parent re-render.

Common Mistake:

Every time the button is clicked, Child is recreated, losing its identity and state.

jsx

// ? Incorrect - Declaring Child Component Inside Parent

function Parent() {

  const [count, setCount] = React.useState(0);

  function Child() {

    console.log("Child component recreated!");

    return <div>Count: {count}</div>;

  }

  return (

    <div>

      <button onClick={() => setCount(count + 1)}>Increment</button>

      <Child />

    </div>

  );

}        

Correct Approach:

Declaring Child outside ensures React treats it as a stable component, even during Parent updates.

jsx

// ? Correct - Declaring Child Outside Parent

function Child({ count }) {

  console.log("Child component rendered!");

  return <div>Count: {count}</div>;

}

function Parent() {

  const [count, setCount] = React.useState(0);

  return (

    <div>

      <button onClick={() => setCount(count + 1)}>Increment</button>

      <Child count={count} />

    </div>

  );

}        

17. Extract and Reuse Logic with Custom Hooks

Picture a handyman bringing the same toolbox for every task. That’s what custom hooks do. They centralize reusable logic across your components.

Common Mistake:

Both components repeat the same logic for fetching data, which is a recipe for inconsistency and redundancy.

jsx

// ? Incorrect - Repeating Logic Across Components

function ComponentA() {

  const [data, setData] = React.useState([]);

  React.useEffect(() => {

    fetchData();

  }, []);

  async function fetchData() {

    const res = await fetch("/api/data");

    setData(await res.json());

  }

  return <div>{data.length} items</div>;

}

function ComponentB() {

  const [data, setData] = React.useState([]);

  React.useEffect(() => {

    fetchData();

  }, []);

  async function fetchData() {

    const res = await fetch("/api/data");

    setData(await res.json());

  }

  return <div>{data.length} items</div>;

}        

Correct Approach:

With a custom hook, you solve the problem once and reuse the solution everywhere.

jsx

// ? Correct - Centralize Logic in Custom Hook

function useFetchData(url) {

  const [data, setData] = React.useState([]);

  React.useEffect(() => {

    async function fetchData() {

      const res = await fetch(url);

      setData(await res.json());

    }

    fetchData();

  }, [url]);

  return data;

}

function ComponentA() {

  const data = useFetchData("/api/data");

  return <div>{data.length} items</div>;

}

function ComponentB() {

  const data = useFetchData("/api/data");

  return <div>{data.length} items</div>;

}        

18. Slim Down Your DOM with Fragments

Think of cleaning up clutter from your workspace. Using fragments removes the unnecessary <div> wrappers that bloat your DOM.

Common Mistake:

Adding a redundant <div> doesn’t serve any functional purpose but clutters the DOM.

jsx

// ? Incorrect - Overusing <div> Wrappers

function App() {

  return (

    <div>

      <h1>Title</h1>

      <p>Content</p>

    </div>

  );

}        

Correct Approach:

Fragments streamline your markup without altering its functionality.

jsx

// ? Correct - Using Fragments

function App() {

  return (

    <>

      <h1>Title</h1>

      <p>Content</p>

    </>

  );

}        

19. Assign Stable Keys When Rendering Lists

React tracks changes in lists using keys. If keys are unstable (like array indexes), React may mix up elements, leading to weird UI bugs.

Common Mistake:

Using index can cause mismatches when list order changes, confusing React's reconciliation process.

jsx

// ? Incorrect - Using Index as Key

function List({ items }) {

  return (

    <ul>

      {items.map((item, index) => (

        <li key={index}>{item.name}</li>

      ))}

    </ul>

  );

}        

Correct Approach:

Unique IDs ensure React can track elements accurately, even if the order or content changes.

jsx

// ? Correct - Using Stable and Unique Keys

function List({ items }) {

  return (

    <ul>

      {items.map((item) => (

        <li key={item.id}>{item.name}</li>

      ))}

    </ul>

  );

}        

20. Persist Non-Reactive Values with useRef

Ever needed a notepad that doesn’t distract you when updated? That’s useRef—ideal for values that change but don’t trigger re-renders.

Common Mistake:

useState here causes re-renders unnecessarily when storing non-reactive data like timers.

jsx

// ? Incorrect - Using useState for Persistent Values

function Timer() {

  const [timerId, setTimerId] = React.useState(null);

  function startTimer() {

    const id = setInterval(() => console.log("Tick"), 1000);

    setTimerId(id);

  }

  return <button onClick={startTimer}>Start Timer</button>;

}        

Correct Approach:

With useRef, you persist the timer ID without triggering unwanted re-renders.

jsx

// ? Correct - Using useRef for Persistent Values

function Timer() {

  const timerId = React.useRef(null);

  function startTimer() {

    timerId.current = setInterval(() => console.log("Tick"), 1000);

  }

  return <button onClick={startTimer}>Start Timer</button>;

}        

21. Spot Issues Early with React's StrictMode

Use React’s StrictMode to identify potential issues like deprecated methods or unexpected side effects during development.

Common Mistake:?

Skipping StrictMode, leaving unsafe methods or side effects unnoticed.

jsx

// ? Wrong: No StrictMode, issues like unsafe methods go unnoticed.

function App() {

  return <MyComponent />;

}

class MyComponent extends React.Component {

  componentWillMount() {

    console.log("This lifecycle method is deprecated!");

  }

  render() {

    return <div>Hello World</div>;

  }

}        

Correct Approach:?

Wrap your entire app or specific components in React.StrictMode during development to catch issues like deprecated methods or unsafe lifecycle usage. This doesn’t affect production builds.

jsx

// ? Correct usage of StrictMode to catch warnings during development.

import React from "react";

import ReactDOM from "react-dom";

function App() {

  return (

    <React.StrictMode>

      <MyComponent />

    </React.StrictMode>

  );

}

function MyComponent() {

  React.useEffect(() => {

    console.log("This effect runs safely");

  }, []);

  return <div>Hello World</div>;

}

ReactDOM.render(<App />, document.getElementById("root"));        

22. Optimize Performance with IntersectionObserver for Lazy Loading

Use the Intersection Observer API for efficient lazy loading of elements, improving performance in media-heavy apps.

Common Mistake:?

Relying on inefficient scroll event handlers for visibility detection.

jsx

// ? Scroll handlers are inefficient and can degrade performance.

function LazyImage({ src }) {

  React.useEffect(() => {

    const handleScroll = () => {

      console.log("Checking visibility...");

    };

    window.addEventListener("scroll", handleScroll);

    return () => window.removeEventListener("scroll", handleScroll);

  }, []);

  return <img src={src} alt="Lazy" />;

}        

Correct Approach:?

Use the Intersection Observer API for elements like images or components to load them only when they are about to enter the viewport.

jsx

// ? Real-life lazy loading with IntersectionObserver.

import React, { useEffect, useRef } from "react";

function LazyImage({ src, alt }) {

  const imgRef = useRef(null);

  useEffect(() => {

    const observer = new IntersectionObserver(

      ([entry]) => {

        if (entry.isIntersecting) {

          imgRef.current.src = src;

          observer.disconnect();

        }

      },

      { threshold: 0.1 }

    );

    if (imgRef.current) observer.observe(imgRef.current);

    return () => observer.disconnect();

  }, [src]);

  return <img ref={imgRef} alt={alt} />;

}

export default function App() {

  return (

    <div>

      <h1>Scroll Down to Load Images</h1>

      <LazyImage src="image1.jpg" alt="Image 1" />

      <LazyImage src="image2.jpg" alt="Image 2" />

    </div>

  );        

23. Simplify Your Code by Splitting Large Components

Break down large components into smaller ones to improve readability, testability, and maintainability.

Common Mistake:?

Bundling too much logic in one component, making it hard to debug or reuse.

jsx

// ? A single bloated component with mixed responsibilities.

function Dashboard() {

  const [users, setUsers] = React.useState([]);

  React.useEffect(() => {

    fetch("/api/users").then((res) => res.json()).then(setUsers);

  }, []);

  return (

    <div>

      <h1>Dashboard</h1>

      <ul>

        {users.map((user) => (

          <li key={user.id}>{user.name}</li>

        ))}

      </ul>

    </div>

  );

}        

Correct Approach:?

Break down complex components into smaller ones to follow the single-responsibility principle and enhance code readability and reusability.

jsx

// ? Splitting a dashboard into smaller components for readability.

function Dashboard() {

  return (

    <div>

      <h1>Dashboard</h1>

      <UserList />

    </div>

  );

}

function UserList() {

  const [users, setUsers] = React.useState([]);

  React.useEffect(() => {

    fetch("/api/users")

      .then((res) => res.json())

      .then(setUsers);

  }, []);

  return (

    <ul>

      {users.map((user) => (

        <li key={user.id}>{user.name}</li>

      ))}

    </ul>

  );

}

export default Dashboard;        

24. Always Assign Initial State for Stability

Assign an initial value to your state variables with useState to avoid runtime errors or undefined values.

Common Mistake:?

Failing to initialize state properly, causing unexpected behavior.

jsx

// ? No initial value leads to undefined errors.

function Profile() {

  const [user, setUser] = React.useState();

  return <div>{user.name}</div>; // Error: Cannot read 'name' of undefined

}        

Correct Approach:?

Always initialize state variables with default values to avoid runtime errors caused by undefined.

jsx

// ? Providing a default state value to avoid runtime errors.

function Profile() {

  const [user, setUser] = React.useState({ name: "" });

  return <div>{user.name || "No user data available"}</div>;

}

export default Profile;        

25. Avoid Overuse of useEffect and setState

Use useEffect for side effects only, and avoid unnecessary setState calls within it to prevent performance issues.

Common Mistake:?

Triggering setState inside useEffect without proper dependency handling, leading to infinite loops.

jsx

// ? Missing dependency array causes infinite re-renders.

function Counter() {

  const [count, setCount] = React.useState(0);

  React.useEffect(() => {

    setCount(count + 1); // Triggers an infinite loop.

  });

  return <div>{count}</div>;

}        

Correct Approach:?

Use useEffect only for side effects, and ensure dependencies are properly managed to avoid unnecessary re-renders or infinite loops.

jsx

// ? Correctly handling state updates and dependencies in useEffect.

function Counter() {

  const [count, setCount] = React.useState(0);

  useEffect(() => {

    console.log(`Count updated: ${count}`);

  }, [count]); // Runs only when count changes.

  const increment = () => setCount((prevCount) => prevCount + 1);

  return (

    <div>

      <p>Count: {count}</p>

      <button onClick={increment}>Increment</button>

    </div>

  );

}

export default Counter;        

26. Simplify State Handling by Using null for Objects

Always initialize the state meant for objects with null instead of an empty object ({}). This simplifies conditional checks for object existence and makes your code easier to read.

Common Mistake:?

Initializing state as an empty object can lead to verbose checks to determine if the object has meaningful data.

jsx

// ? Wrong: Using an empty object complicates existence checks.

const [user, setUser] = React.useState({});

function Profile() {

  return (

    <div>

      {Object.keys(user).length === 0 ? (

        <p>No user found</p>

      ) : (

        <p>{user.name}</p>

      )}

    </div>

  );

}        

Correct Approach:?

Initialize state with null for cleaner and simpler existence checks. It simplifies conditions by relying on null as a straightforward indicator of no data.

jsx

// ? Correct: Using null simplifies existence checks.

const [user, setUser] = React.useState(null);

function Profile() {

  return (

    <div>

      {user ? <p>{user.name}</p> : <p>No user found</p>}

    </div>

  );

}        

27. Use a ContextProvider Component for Efficient Context Management

Instead of passing the value prop directly to Context.Provider, wrap it in a dedicated ContextProvider component to avoid unnecessary re-renders of child components.

Common Mistake:?

Passing value directly to Context.Provider can lead to child components re-rendering unnecessarily.

jsx

// ? Wrong: Directly passing value causes unnecessary re-renders.

const App = () => {

  const [user, setUser] = React.useState(null);

  return (

    <AuthContext.Provider value={{ user, setUser }}>

      <ComponentA />

      <ComponentB />

    </AuthContext.Provider>

  );

};        

Correct Approach:?

Create a ContextProvider to isolate context logic and prevent unwanted re-renders. It encapsulates context logic in a reusable component, ensuring only components using the useContext hook re-render when context changes.

jsx

// ? Correct: ContextProvider minimizes re-renders of child components.

export const AuthContextProvider = ({ children }) => {

  const [user, setUser] = React.useState(null);

  return (

    <AuthContext.Provider value={{ user, setUser }}>

      {children}

    </AuthContext.Provider>

  );

};

const App = () => (

  <AuthContextProvider>

    <ComponentA />

    <ComponentB />

  </AuthContextProvider>

);        

28. Ensure Stability by Properly Managing useEffect Dependencies

Always include all external variables referenced inside useEffect in its dependency array to avoid bugs caused by stale closures.

Common Mistake:?

Forgetting to include dependencies in the array leads to stale or incorrect values being used.

jsx

// ? Wrong: Missing dependencies can cause bugs due to stale data.

const Counter = () => {

  const [count, setCount] = React.useState(0);

  React.useEffect(() => {

    console.log(`Count is now: ${count}`); // Stale closure issue

  }, []); // Missing count in dependency array

  return <button onClick={() => setCount(count + 1)}>Increment</button>;

};        

Correct Approach:?

Include all variables in the dependency array to ensure up-to-date behavior. It prevents bugs by ensuring the effect runs with the latest values of all referenced variables.

jsx

// ? Correct: Dependencies ensure effect runs with fresh values.

const Counter = () => {

  const [count, setCount] = React.useState(0);

  React.useEffect(() => {

    console.log(`Count is now: ${count}`);

  }, [count]); // Correctly includes count in the dependency array

  return <button onClick={() => setCount(count + 1)}>Increment</button>;

};        

29. Build Flexible UIs with Compound Components

Use compound components to build reusable and customizable UI elements that can be composed declaratively by consumers.

Common Mistake:?

Hardcoding component logic makes UIs less flexible and harder to reuse.

jsx

// ? Wrong: Hardcoded structure limits flexibility.

function Modal({ isOpen, children }) {

  if (!isOpen) return null;

  return <div className="modal">{children}</div>;

}        

Correct Approach:?

Use compound components to allow flexible composition. It enables consumers to define the structure and content of the modal declaratively.

jsx

// ? Correct: Compound components allow flexible UI composition.

const Modal = ({ children }) => <div className="modal">{children}</div>;

Modal.Header = ({ children }) => <div className="modal-header">{children}</div>;

Modal.Body = ({ children }) => <div className="modal-body">{children}</div>;

Modal.Footer = ({ children }) => <div className="modal-footer">{children}</div>;

// Usage

const App = () => (

  <Modal>

    <Modal.Header>Title</Modal.Header>

    <Modal.Body>Content</Modal.Body>

    <Modal.Footer>Footer</Modal.Footer>

  </Modal>

);        

30. Avoid Unintended Rendering with Logical AND in JSX

Avoid using && with non-boolean values in JSX to prevent rendering unexpected values like 0.

Common Mistake:?

Using && directly with expressions like users.length can cause unintended values to render.

jsx

// ? Wrong: Displays 0 if users.length is 0.

const Users = ({ users }) => (

  <div>{users.length && <p>We have users!</p>}</div>

);        

Correct Approach:?

Use explicit conditions to avoid unintended rendering. It ensures only the desired content renders, avoiding issues with falsy values like 0.

jsx

// ? Correct: Proper checks prevent rendering of unintended values.

const Users = ({ users }) => (

  <div>{users.length > 0 && <p>We have users!</p>}</div>

);        

Conclusion?

React development is a journey of continuous learning and improvement. While some of these tips might feel like fundamentals and others’ game-changers, they all serve to sharpen your skills and make you a more efficient and confident developer. Remember, the React ecosystem is vast, and there's always more to explore.

You might have found some tips valuable and some very basic. There are more valuable, important, and helpful tips and best practices out there,? but I found these tips to be a must-have in your knowledge library.?

However, you can share other React tips in the comments, and help others skip the harder times during coding and developing.

要查看或添加评论,请登录