Our Blog:

React Refresh: The Frontend Developer's Guide


All
Andrej Vajagic

Andrej Vajagic

11.12.2023, 13:42

Read time: undefined mins

React Refresh: The Frontend Developer's Guide

1. Fundamentals of React

Components & Props:

Class-based Components

Definition: Class-based components are more traditional React components declared using ES6 classes. They extend from React.Component.
State Management: These components can hold and manage local state.
Lifecycle Methods: They provide lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount for handling side effects.
Syntax Example: code

Functional Components

Definition: Functional components are simpler and declared using functions. They became more powerful with the introduction of Hooks in React 16.8
State and Effects: Hooks like useState and useEffect allow functional components to handle state and side effects, traditionally the realm of class components.
Simpler and Concise: They tend to be more concise and easier to read and test.

Syntax Example: code

Props (Properties)

Role in Components: Props are how data is passed to components. They are read-only, meaning a component can only read the props passed to it but cannot modify them.
Parent-Child Communication: Props allow parent components to pass data down to their children components. This is a fundamental aspect of React's data flow.
Usage in Class-based Components: Accessed via this.props.
Usage in Functional Components: Accessed directly from the function’s parameters.
Example of Passing Props code

In this example, the App component is passing a prop named name with the value "Jane" to the Welcome component. The Welcome component then accesses and uses this prop.

State & Lifecycle:

useState Hook

Purpose: useState allows you to add state to functional components. Before hooks, state was only available in class components.

Syntax & Usage: code

state is the current state.
setState is a function to update the state.
initialState is the initial value of the state.

Example: code

When you need to update the state based on the current state, you should use a functional update. This is particularly important when the new state is derived from the current state.

code

useState(0) initializes the count state variable with the initial value of 0.
setCount is the function used to update the count state.
increment is a function that takes an amount to add to the current count.
Inside increment, setCount is called with a function that takes the current state (currentCount) and returns the updated state.

useEffect Hook

Purpose: useEffect lets you perform side effects in functional components. It is a replacement for lifecycle methods like componentDidMount, componentDidUpdate, and componentWillUnmount in class components.
Syntax & Usage: code

The function passed to useEffect runs after the render is committed to the screen.
The optional return function is for cleanup, akin to componentWillUnmount.
The dependencies array is optional. If it's present, the effect runs only if the dependencies have changed between renderings.

Example: code

Tips for Mastering useState and useEffect

  • Understand Closure: useState and useEffect heavily rely on JavaScript closures. Ensure you understand how closures work to avoid common pitfalls. You can find more here
  • Managing Complex State: For complex state logic, consider using the useReducer hook, which is more suited for state transitions that involve multiple sub-values or when the next state depends on the previous one.
  • Optimizing Performance: In useEffect, carefully manage your dependencies. Include all values from the component scope that change over time and are used by the effect.
  • Cleanup in useEffect: Always think about cleaning up, especially for subscriptions, listeners, or any side effects that might lead to memory leaks.

More hooks:

1. useContext Hook

Purpose: Simplifies the process of passing data through the component tree without having to pass props down manually at every level (known as "prop drilling").
Usage:
Create a context using React.createContext().
Provide Context Value with UserContext.Provider
Use useContext hook to access the context value in a functional component.
Example: code code code

More about it here

2. useReducer Hook

Purpose: An alternative to useState, ideal for managing more complex state logic that involves multiple sub-values or when the next state depends on the previous one.
Usage:
Takes a reducer function and an initial state.
Returns the current state and a dispatch method.
Example: code

More about it here

3. useMemo Hook

Purpose: Memoizes expensive calculations. It recalculates the value only when one of its dependencies changes.
Usage:
Takes a function and an array of dependencies.
Returns a memoized value.
Example: code

4. useCallback Hook

Purpose: Returns a memoized callback function. Useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.
Usage:
Takes a function and an array of dependencies.
Returns a memoized version of the callback.
Example: code

More about it here

Best Practices & Tips

  • useContext: Use it judiciously as it might lead to unnecessary re-renders if the context value changes often.
  • useReducer: Prefer it over useState when you have complex state logic or when the next state depends on the previous one.
  • useMemo and useCallback: Use them to optimize performance but be cautious. Overusing them, especially in scenarios where the dependencies change often, can lead to worse performance due to the overhead of memoization.

Refs:

React refs (short for references) are a feature in React that allows you to access DOM nodes or React elements created in the render method. Refs provide a way to directly interact with an element, which is something you generally want to avoid in React (since React favors a declarative approach to UI), but they are necessary for certain tasks.

When to Use Refs

  • Managing Focus, Text Selection, or Media Playback: For example, automatically focusing on a particular input when a form loads.
  • Triggering Imperative Animations: Refs can be used to trigger animations directly on a DOM node.
  • Integrating with Third-party DOM Libraries: Useful when you need to use a React component to manage a DOM element that is controlled by a jQuery plugin, for example.

Creating Refs:

Use the useRef hook. code

2. Advanced React Concepts

Custom hooks

Custom hooks are JavaScript functions whose name starts with ”use” and that may call other hooks. They're a mechanism to reuse stateful logic (such as setting up a subscription or remembering the current value of a prop), but every time you use a custom hook, all state and effects inside of it are fully isolated.

Why Use Custom Hooks?

  • Reusability: Share logic across multiple components without duplicating code.
  • Separation of Concerns: Break down complex components by abstracting common functionality into separate hooks.
  • Readability: Make components easier to read and maintain by abstracting complex logic away.
  • Organization: Custom hooks offer a way to organize related logic neatly.

Creating a Custom Hook

Let's create a simple custom hook called useFormInput to handle form inputs.

code

Using a Custom Hook

You can use this useFormInput hook in any component that needs to handle form input. code

Best Practices for Custom Hooks

  • Naming Convention: Always start the name of your custom hooks with use to follow React's convention and ensure proper hooks behavior.
  • Isolation of State: Each time you use a custom hook, the state and effects inside of it are completely isolated. Remember that they don’t share state.
  • Keep Them Focused: Custom hooks should ideally be focused on a specific task or set of related tasks.
  • Test Your Custom Hooks: Ensure that your custom hooks are reliable and maintainable by writing unit tests for them.
  • Document Usage: Especially if you're creating hooks that will be used by others, document how to use them.

Higher Order Components (HOCs):

  • Prefer Hooks for Logic Reuse: Whenever possible, use hooks to reuse logic across components, as they tend to be more straightforward and easier to follow than HOCs.
  • Use HOCs Judiciously: Use HOCs in scenarios where they provide clear advantages, such as enhancing components in a way that hooks cannot, or when dealing with class components that cannot use hooks.
  • Avoid Nested Wrappers: Both HOCs and hooks can lead to deeply nested structures if overused. Be mindful of this and aim for simplicity and readability.

In summary, while HOCs are still a valid pattern in React, the introduction of hooks and custom hooks has provided a more flexible and often simpler way to share logic across components. This shift encourages a more functional approach to React development, aligning with the overall direction of the framework.

3. State Management

Context API:

The Context API in React provides a way to pass data through the component tree without having to pass props down manually at every level. Deciding when to use the Context API over more robust state management libraries like Redux is crucial for maintaining the simplicity and performance of your application. Here are key considerations:

Use Context API When:

  • Prop Drilling Becomes Cumbersome: If you're passing props through many layers of components (prop drilling) and it's solely for the purpose of reaching deeply nested components, Context can be a cleaner solution.
  • Simple Global State Needs: When your application has a global state that is simple and doesn't involve complex logic or deep nested updates. For instance, user authentication status, theme settings, or UI preferences.
  • Small to Medium-Sized Apps: For applications that are not too large and where state management needs are limited, the Context API can be sufficient and easier to implement compared to heavier state management solutions.
  • Performance Isn't a Major Concern: While Context API is efficient, it can lead to unnecessary re-renders if not used wisely. If your app isn't highly performance-sensitive, this may be an acceptable trade-off.
  • Less Boilerplate Code: If you want to avoid the verbosity and boilerplate code that often comes with solutions like Redux.

Prefer State Management Libraries When:

  • Complex State Logic: If your application involves complex state logic, like handling a large amount of data with intricate updates, or if there are many interdependencies between different parts of the state.
  • Performance Optimization: Libraries like Redux offer optimizations like memoization and selective rendering, which can be crucial for high-performance applications.
  • DevTools and Middleware: If you need advanced features like time-travel debugging, middleware support, or other extensions provided by libraries like Redux.
  • Large-Scale Applications: For larger applications, where state management can get overwhelmingly complicated, using a dedicated state management library can provide better structure and maintainability.
  • Familiarity and Ecosystem: If your development team is already familiar with a particular state management library, and there are existing utilities or middleware that fit your application needs.

Redux:

More about it here

6. Performance Optimization

Code Splitting:

Lazy loading components in React is a powerful feature for optimizing the performance of your application, especially for larger apps. It enables you to split your code into chunks and load them on demand, rather than loading everything upfront. This can significantly improve the user experience by reducing the initial load time. React provides built-in support for lazy loading with React.lazy and Suspense.

React.lazy

Purpose: React.lazy is a function that lets you render a dynamic import as a regular component.
Usage: It is used for default exported components.
Syntax: code

Suspense

Purpose: Suspense lets your components “wait” for something before rendering, typically used with React.lazy to specify a loading state (like a spinner) while waiting for the lazy component to load.
Usage: Wrap lazy components in a Suspense component.
Syntax: code

Best Practices and Tips

  • Use for Heavy Components: Ideal for components that are large and not immediately needed.

  • Error Handling: As of React 17, if the module loading fails (such as due to network problems), it will trigger an error. You can handle these errors to improve user experience.

  • Server-Side Rendering: Suspense and React.lazy don't yet support server-side rendering. If you're server-side rendering a React app, you'll need to use a different method of code-splitting. NextJs offer something similar

  • Named Exports: If you need to lazily load a component with named exports, you will need to create an intermediate module that reexports it as the default. For example: code

  • Suspense Fallback: The fallback prop of Suspense can be any React element, providing flexibility for the loading UI.

  • Bundling: Ensure your bundler supports code splitting (like Webpack, Parcel).

Memoization:

More about it here

Use of Web Workers:

Using Web Workers in a React application can be a strategic decision for performance optimization. Web Workers allow you to run JavaScript in background threads, enabling heavy computations or complex processing without blocking the UI thread. This is especially beneficial in web applications where maintaining a responsive interface is crucial.

When to Use Web Workers in a React App

  • Handling Intensive Computations: If your app performs heavy calculations, like image processing, complex mathematical calculations, or large data manipulation, using Web Workers can prevent UI freezes.

  • Offloading Non-UI Work: Tasks that are not related to the UI but require significant processing power are ideal candidates for Web Workers. For example, parsing large JSON files or complex algorithmic processing.

  • Background Data Processing: When you need to process data in the background (like sorting large arrays or filtering datasets) while keeping the UI responsive.

  • Real-time Applications: Applications like games, real-time charts, or simulations that require high-frequency updates and calculations can benefit from Web Workers.

  • Progressive Web Apps (PWAs): PWAs that require background sync or data processing can utilize Web Workers to handle these tasks without affecting the main thread.

How to Use Web Workers in React

  • Creating a Web Worker: You typically create a separate JavaScript file for the worker's code. In this file, you define the task that should be performed in the background.

  • Integrating with React: In your React component, create a new Web Worker instance and point it to your worker script. Use postMessage to send data to the worker and onmessage to receive results from the worker.

  • Example: code

Best Practices

  • Avoid Overusing Workers: Creating too many workers can lead to resource contention. Use them judiciously for tasks that truly benefit from background processing.
  • Data Transfer Considerations: Remember that data passed between the main thread and workers is copied, not shared. This can have performance implications for large data sets.
  • Error Handling: Implement error handling in workers to ensure stability.
  • Terminate When Done: Always terminate a worker when it's no longer needed to free up resources.

7. React Ecosystem and Tools

TypeScript with React:

More about it here

Styling in React:

More about it here

Share:

Accelerating Digital Success. Experience the future of web development – faster, smarter, better. Lets innovate together.

©2024 Dreit Technologies | All rights reserved

Contact

  • +38 64 577 3034
  • Serbia
  • Marka Pericina Kamenjara 11A, Ruma
  • Contact us