7 Common Mistakes to Avoid When Using React

    Tuesday, March 26, 202410 min read273 views
    7 Common Mistakes to Avoid When Using React

    React development has gained huge popularity over the years and developers have started using React as their most preferred framework due to its flexible nature and efficiency.

    Building user interfaces and top-quality dynamic React applications has become quite easy using React framework. While using the framework, developers may make some common mistakes or face certain errors that may affect their development process.

    Based on the experience of our React Developers and many other developers, we have chalked down the most common mistakes to avoid in React. This will help you to keep yourself away from creating these mistakes and have a great working experience.

    So even if you are a learner or a skilled developer, this blog is super helpful for you.

    What is ReactJS?

    React is a front-end and open-source Javascript library that is useful in developing user interfaces, specifically for applications with a single page.

    It helps React developers build complex and reusable components of web and mobile applications. Functional components can also be built using TypeScript.

    The Important Features of React

    • It supports server-side rendering.

    • It will make use of virtual DOM(document object model) rather than real DOM as real DOM manipulations are expensive.

    • It follows unidirectional data flow or data binding.

    • It uses reusable or composable UI components for developing the view.

    React is essential for creating applications that exhibit optimal performance and retain ease of maintenance throughout their development lifecycle. Many different errors can occur while working with any React component.

    This blog post explores 7 common mistakes that developers might encounter when working with React.

    Table of Contents

    1. Unnecessarily using useEffect

    2. Setting incorrect value types for the initial state

    3. Writing business logic inside reusable components

    4. Not writing clean up function within useEffect

    5. Poor component structure

    6. Not creating enough components

    7. Not using keys correctly in Lists

    Unnecessarily Using useEffect

    With the introduction of simultaneous rendering in React 18, it is crucially important to use hooks such as useEffect correctly. Unnecessarily using useEffect can cause such code to have performance issues everywhere, side effects, and hard-to-debug problems.

    React developers should solely use it to perform side effects that impact the outside world, like fetching data from a server, subscribing to an event, or updating the DOM. These side effects can often be asynchronous and may occur unpredictably, making it necessary to create robust how to manage them in our components.

    However, when we only need to perform side effects that influence the component's internal state or don't affect the state directly in the outside world, we can use other hooks or plain JavaScript instead of useEffect. The updated React documentation provides a comprehensive list of situations where useEffect may not be required and how we can replace it.

    Setting Incorrect Value Types for the Initial State

    A common mistake made by React developers is setting the initial state value of an object or array to null or an empty string, and then attempting to access the properties of that value during render.

    Similarly, an error occurs if we are not providing default state values for nested objects, and trying to access them in the render method or other component methods.

    Consider a UserProfile component where we fetch the user's data from an API and display a greeting with the user's name.

    An error would occur if we attempt to render this component: Cannot read property 'name' of null.

    import { useEffect, useState } from "react";
    export function UserProfile() {
     const [user, setUser] = useState(null);
     useEffect(() => {
       fetch("/api/profile").then((data) => {
         setUser(data);
       });
     }, []);
     return (
       <div>
         <div>Welcome, {user.name}</div>
       </div>
     );}

    A similar problem occurs when setting the initial state value to an empty array and then trying to access the n-th term from it. Until the data is fetched by an API call, the component renders with the initial state provided to it.

    Attempting to access a property on a null or undefined element will cause an error. Consequently, it's crucial to ensure the initial state closely represents the updated state. In our previous example, the only correct way of state initialization should be:

                                                                                         

    import { useEffect, useState } from "react";
    export function UserProfile() {
     const [user, setUser] = useState({ name: "" });
     useEffect(() => {
       fetch("/api/profile").then((data) => {
         setUser(data);
       });
     }, []);
     return (
       <div>
         <div>Welcome, {user.name}</div>  // Renders without errors
       </div>
     );}

    Writing Business Logic Inside Reusable React Components

    Writing business logic within reusable components is a common mistake that React developers should avoid. This practice can make your code difficult to read, maintain, and scale and may lead to unexpected behavior.

    While building React applications there is a simple way to make your application clean, scalable, and easily testable. In react we divide our application into many small reusable components.   

    Reusable components are the components that contain only HTML and JSX. Along with that, we have three different layers for different purposes.

    Three Layers of Responsibilities

    Presentational components

    Components that just render HTML and are primarily concerned with how things look.

    Business logic

    logically defined by the business/client (like manipulating data: sorting, calculations, etc)

    Implementation logic

    intermediate logic that makes your app work, and connects business logic to your presentational components (manipulating the DOM, managing state, changes, network requests, etc). This is usually done using frameworks.

    Writing Business Logic Inside Reusable React Components Leads to Unexpected Behavior

    Code Clutter

    Writing business logic inside reusable components results in code clutter (too much code in a single file). This decreases the readability of the code.

    Reusability

    Components embedded with business logic are harder to reusable. If the same logic is needed in multiple components it can lead to code duplication and maintainability of the code.

    Difficulty in Testing 

    Unit testing becomes more challenging if business logic is written inside the component. Unit testing particularly tests about presentational layer r.

    Scalability Issue  

    As the application grows, having business logic spread in an array based on numerous separate components, can make it very difficult to scale the application

    Not Writing Clean Up Function Within useEffect

    Let’s first get a brief overview of the useEffect hook in React hooks. useEffect hook is used to manage the lifecycle methods of the component name the functional components.

    Similar to these componentDidMount, componentDidUpdate, and componentWillUnmount lifecycle methods of react component of the class component.

    Now, let’s discuss what is clean up function in useEffect is and its importance. The Cleanup function is similar to the componentWillUnmount lifecycle method.

    It will execute after the component gets unmounted and clears all the timers, intervals, or any events that we register during the component mounting process. It is necessary to clear timers, intervals, and events when we are unmounting the component from the DOM.

    Here is one example that describes the importance of cleanup in useEffect.

     const TimeComponent = () => {
      useEffect(() => {
         setInterval(() => console.log("This will log after every 2 seconds"),          
         2000);
      }, []);
      return <h1>This is timer component</h1>;
     }
     const NormalComponent = () => {
      return <h1>This is normal component.</h1>;
     }
     const ParentComponent = () => {
      const [show, setShow] = useState("timer");
      const handleClick = (type) => {
        setShow(type);
      }
     return (
          <div>         <button onClick={() => { show === "timer" ? handleClick("normal") :      
                  handleClick("timer") }}>Show Component</button>            
                  {show === "timer" && <TimeComponent />} 
                 {show === "normal" && <NormalComponent />} 
         </div>
      );
     }

    The code snippet has one issue, we will get this log (This will log after every 2 seconds) in the console after every two seconds. Even though we are not showing the TimeComponent.

    You can see in the console, (This will log after every 2 seconds)  this is logging when we have rendered NormalComponent.

    But we haven’t written any console or any setInterval inside the NormalComponent.

    So, how to control this unexpected behavior? By writing a clean-up function within useEffect. Yes, just a little bit of modification in the above useEffect hook and an example of how we can solve it.

     useEffect(() => {
       const timer = setInterval(
         () => console.log("This will log after every 2 seconds"),
         2000
       );
       return () => clearInterval(timer);
     }, []);

    There is no log-in console as we have written the clean-up function in useEffect.

    This will clear setInterval when TimerComponent gets unmounted. Now we will not see that log in the console when we are showing NormalComponent 

    Poor Component Structure

    When working with React, the structure of your components plays an important role in the maintainability and scalability of your application.

    A poor component structure can lead to unexpected behavior and difficulties in understanding, debugging, and extending the codebase. 

    The folder structure generated by ‘npx create-react-app’ serves as a quick and convenient starting point for many React developers.

    However, some developers may find it less suitable for larger or more complex projects due to its simplicity and the absence of clear separation between components, services, and other concerns.

    Challenges with the default ‘create-react-app’ structure : 

    • Flat structure: become less manageable as the project grows.

    • Limited Separation of Concerns: There's less emphasis on separating components, services, and styles into dedicated directories.

    • Global styles in ‘src/index.css: Global styles are often placed in a single index.css file.

    • Lack of Explicit Service or Utility Directories:  There isn't a designated directory for services or utility functions.

    • No Distinction Between Presentational and Container Components

    Default Folder Structure

    default folder structure

    Improved folder Structure

    improved folder structureImproved folder structure 2nd image

    Developers may customize this structure based on their project requirements, but the goal is to have a clear and organized layout that supports the long-term maintainability and scalability of the React application. 

    Not Creating Enough Components 

    React developers make a common mistake, they don’t create enough components. React is famous for its component-based architecture.

    With React, you can create large components that execute many tasks, but it's better to keep components small with one component corresponding to one function.

    By creating enough components or dividing a large multi-purpose component into smaller components that hold only a single purpose, a developer can not only save time but also improve the code quality and maintainability.

    The first code snippet displays the approach where we are not splitting code into multiple components and writing everything in a single component. 

    This doesn’t seem like a problem until your component becomes substantial. As your application grows, with these all-inclusive components it can be very difficult to manage

     const TodoList = () => {
      const [todo, setTodo] = useState([]);
      const [newTodo,setNewTodo] = useState("")
      const addTodo = (newTodo) => {
        setTodo([...todo, newTodo]);
      };
     return (
       <div>
         <h1>Todo List</h1>
         <input
           type="text"
           value={newTodo}
           onChange={(e) => setNewTodo(e.target.value)}
         />
         <button onClick={addTodo}>Add Todo</button>
         <ul>
           {todo.map((todo, index) => (
             <li key={index}>{todo} </li>
           ))}
         </ul>
       </div>
      );
     };

    Here, the code snippet shows us how to create a component for a logic that is repetitive and we need to reuse it at several places in our code.

    We have created a TodoInput component that holds the plain HTML input and adds todo logic, now onwards we don’t need to write it every time. It can be done by simply importing the TodoInput component.

    Secondly, we also created a TodoItem component for the rendering of todo items

     const TodoList = () => {
      const [todo, setTodo] = useState([]);
      const addTodo = (newTodo) => {
       setTodo([...todo, newTodo]);
      };
     return (
       <div>
         <h1>Todo List</h1>
         <TodoInput onAdd={addTodo} /> 
        <ul>
           {todo.map((todo, index) => (
             <TodoItem key={index} todo={todo} /> 
         ))}
         </ul>
       </div>
      );
     };

    Not Using Keys Correctly in Lists

    A key is a special string attribute that needs to be included when using lists of elements.

    Importance of Keys -:

    • Keys help react to identify which elements were added, changed, or removed.

    • Keys should be given to array elements to provide a unique identity for each element.

    • Without keys, React does not understand the order or uniqueness of each element.

    • With keys, React has an idea of which particular element was deleted, edited, and added.

    • Keys are generally used for displaying a list of data coming from an API.

    Example of a List Using Key in the Right Way:

     const ids = [1, 2, 3, 4, 5];
    const listElements = ids.map((id) => {
      return <li key={id.toString()}>{id}</li>;
     });

    In Conclusion

    In conclusion, by steering clear of the common mistakes discussed in this article, developers can enhance the overall quality of their code and ensure that your react code base is efficient, reliable, and easy to maintain.

    Becoming a proficient React developer involves not only mastering the core concepts but also avoiding the frequent mistakes that impede your progress.

    So, with patience and perseverance, you can master React and use it to create applications that delight your users and solve real-world problems. I hope you find the information helpful. Thank you for reading.

    24

    Related articles

    This website uses cookies to analyze website traffic and optimize your website experience. By continuing, you agree to our use of cookies as described in our Privacy Policy.