ai-cfia / fertiscan-frontend

This repository is for the frontend of the project react
MIT License
2 stars 1 forks source link

(Refactor) Transition from useState to Redux for State Management #204

Open SamuelPelletierEvraire opened 1 month ago

SamuelPelletierEvraire commented 1 month ago

Description

This task concentrates on refactoring the FertiScan project by replacing instances of the useState hook with a robust Redux state management solution. This overhaul aims to enhance state management scalability and maintainability across the application. In addition to the code changes, comprehensive documentation of the implementation process is required to guide current and future developers.

Task

Acceptance criteria

Parent task

Ensure that all prerequisite or parent tasks are completed before commencing this task.

192

Child tasks

Next task to do #

SamuelPelletierEvraire commented 1 month ago

Tutorial for State Management in React: Migrating from useState to Redux

Introduction to State Management with Redux

Effective state management forms the cornerstone of React application development. Initially, the useState hook may suffice for managing local state within components, but as an application's complexity scales, a more robust system is often required. Redux provides a centralized state container that addresses the challenges of more demanding scenarios, improving predictability and maintainability of the application's state. In this tutorial, we will explore the advantages of Redux over useState and guide you through a step-by-step example of migrating to Redux.

Transitioning to Redux: A Step-by-Step Migration

Let's take a simple counter application as an example to demonstrate the transition process from useState to Redux.

Counter Component with useState

import React, { useState } from 'react';

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

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

export default Counter;

Steps to Implement Redux

Installing Redux Dependencies

npm install redux react-redux

Setting Up the Redux Store

First, create your store.js:

import { createStore } from 'redux';

function counterReducer(state = { count: 0 }, action) {
  switch (action.type) {
    case 'INCREMENT':
      return { ...state, count: state.count + 1 };
    default:
      return state;
  }
}

export default createStore(counterReducer);

Providing Redux Store in React

Then, inside your root component:

import { Provider } from 'react-redux';
import store from './store';
import Counter from './Counter';

function App() {
  return (
    <Provider store={store}>
      <Counter />
    </Provider>
  );
}

export default App;

Connecting the Counter Component to Redux

Finally, update your Counter component:

import React from 'react';
import { useSelector, useDispatch } from 'react-redux';

function Counter() {
  const count = useSelector(state => state.count);
  const dispatch = useDispatch();

  return (
    <div>
      <p>{count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>
        Increment
      </button>
    </div>
  );
}

export default Counter;

Optimizing Application Performance

Managing performance encompasses rendering and state. Here's how optimization might look within a React application.

Optimizing Render Performance Example

Under-performing component updates can be addressed with React.memo for functional components or shouldComponentUpdate for class components.

Enhancing State Management Performance

With useState, top-level state changes can result in suboptimal renders. Transition to Redux and using the reselect library for selector memoization can help curtail unnecessary re-renders.

Utilizing Performance Profiling

To diagnose performance challenges, employ React DevTools' profiler to pinpoint slow rendering components or sequences.

Implementing Code Splitting

Improve initial load times by employing React.lazy and Suspense to code split your application, loading components only as needed.

By adopting these strategies, you can tackle a range of performance-related issues, paving the way for a more responsive React application. This tutorial has equipped you with the knowledge to transition from useState to Redux and has empowered you to enhance your application's performance and scalability.

Harnessing Performance Improvements

Efficiency in web applications splits into render performance and state management robustness. Below are some concrete illustrations of how performance can be fine-tuned across these domains.

Performance in web applications can be broadly divided into two aspects: rendering performance and state management performance. Here are some examples that demonstrate how performance can be optimized in both areas.

Rendering Performance Example

Problem: Suppose a React component is re-rendering too often, causing a decrease in performance.

Solution: You can use React.memo for a functional component or shouldComponentUpdate lifecycle method for class components to prevent unnecessary re-renders.

// Functional component with React.memo
const MyComponent = React.memo(function MyComponent({ prop }) {
  // The component only re-renders if prop changes
  return <div>{prop}</div>;
});

// Class component with shouldComponentUpdate
class MyComponent extends React.Component {
  shouldComponentUpdate(nextProps, nextState) {
    // Only re-render if the prop has changed
    return nextProps.prop !== this.props.prop;
  }

  render() {
    return <div>{this.props.prop}</div>;
  }
}

State Management Performance Example

Problem: An application using useState experiences performance issues because changes in the state at the top-level component cause massive re-renders in child components, even when it's not necessary for them to update.

Solution: Migrate state management to Redux and use reselect to memoize selectors, thereby preventing child components from re-rendering unless specific pieces of the state they depend on have changed.

import { createSelector } from 'reselect';

// Suppose your Redux state structure looks like this:
// state = {
//   user: { name: 'Alice', age: 30 },
//   posts: { ... }
// }

// Create a memoized selector using `reselect`
const selectUserName = createSelector(
  state => state.user.name,
  name => name
);

const MyProfileComponent = () => {
  // This component only re-renders when the user's name changes.
  const userName = useSelector(selectUserName);

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

Performance Monitoring Example

Problem: Your application seems to be slow, but you're not sure which components or processes are causing the lag.

Solution: Use performance monitoring tools like the React DevTools profiler to identify bottlenecks.

# Add React DevTools to your project
npm install --dev react-devtools

After installing, you can run the react-devtools and use the profiling capabilities to check which components are taking the longest to render, and optimize accordingly.

Code Splitting Example

Problem: A large bundle size is causing your application to load slowly, particularly on first page load.

Solution: Implement code-splitting using React.lazy and Suspense to break up your app into smaller chunks that can be loaded on-demand.

import React, { Suspense, lazy } from 'react';

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

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}