Pages

Showing posts with label React app development. Show all posts
Showing posts with label React app development. Show all posts

How React hooks Completely replace redux?

Since the introduction of the React hooks API, many questions have been raised about whether or not React hooks will replace Redux.


The overlap between hooks and redux is minimal.


Without utilizing class components, you may leverage React capabilities like state and lifecycle functions with React hooks. Any JavaScript application can use Redux, a state management library that is independent of JavaScript. Depending on the requirements of the application, Redux and React hooks can be used independently or in combination. Each has benefits and drawbacks of their own.





Hooks did not grant us magical new state capabilities. Instead, it improved the API for functions that React already allowed us to perform. However, the hooks API has greatly improved the usability of the native React state API, and because it is simpler than the class model it replaces, I utilise component state far more frequently now than I previously did when it makes sense.


We need a clearer understanding of why we might consider Redux in the first place before we can comprehend what I mean by that.


What is Redux?


Redux is a dependable state management framework and library that interacts well with React.


Redux's main selling factors include:


  • Resolution of deterministic states (enabling deterministic view renders when combined with pure components).

  • state of a transaction.

  • Separate state management from side effects and I/O.

  • only reliable source for application state information.

  • State can be easily shared across several components.

  • Transaction monitoring (auto-logging action objects).

  • repairing time travel bugs.


Redux, in other terms, endows you with superhuman debugging and code organisation abilities. It makes it simpler to create better maintainable code and much simpler to find the primary source of a problem.


What are React Hooks?


React Hooks are unique functions that let React components connect to particular React functionalities. They provide state and other React capabilities without the need for class creation. In React 16.8, hooks are a brand-new feature. You can remove stateful logic from a component using hooks so that it can be tested separately and used again. Additionally, hooks offer a more direct API to React concepts like props, state, context, refs, and lifecycle events that you are already familiar with.


React hooks' main selling points are:


  • Without requiring a class, use state to hook into the component lifecycle.

  • Instead of dividing the functionality amongst different lifecycle methods, group relevant logic together in one location in your component.

  • Share reusable actions without regard to how a component is implemented (like the render prop pattern).


Note that these wonderful advantages don't actually overlap with Redux's advantages. The deterministic state model of Redux is easy to combine with the hooks of React, which can and should be used to get deterministic state updates. This is how React makes deterministic view rendering possible and is one of the main inspirations behind the development of React.


There is no need to select one over the other with tools like the useReducer hook in React and the react-redux hooks API. Apply both. blend and match


When to Use Hooks


Redux is not necessarily necessary for every project or component. I can't think of a decent reason to add Redux's complexity if your app only has one view, doesn't save or load state, and doesn't use asynchronous I/O.


Additionally, if your component


  • Utilises no network.

  • Does not load or save state.

  • Doesn't share state with other components that aren't children.

  • Does require a transient local component state.

The component state concept that comes with React may be useful in your application. In certain circumstances, React hooks will be quite useful. The local component state useState hook from React, for instance, is used in the following form.



import React, { useState } from 'react';

import t from 'prop-types';

import TextField, { Input } from '@material/react-text-field';

const noop = () => {};

const Holder = ({

  itemPrice = 175,

  name = '',

  email = '',

  id = '',

  removeHolder = noop,

  showRemoveButton = false,

}) => {

  const [nameInput, setName] = useState(name);

  const [emailInput, setEmail] = useState(email);

const setter = set => e => {

    const { target } = e;

    const { value } = target;

    set(value);

  };

return (

    <div className="row">

      <div className="holder">

        <div className="holder-name">

          <TextField label="Name">

            <Input value={nameInput} onChange={setter(setName)} required />

          </TextField>

        </div>

        <div className="holder-email">

          <TextField label="Email">

            <Input

              value={emailInput}

              onChange={setter(setEmail)}

              type="email"

              required

            />

          </TextField>

        </div>

        {showRemoveButton && (

          <button

            className="remove-holder"

            aria-label="Remove membership"

            onClick={e => {

              e.preventDefault();

              removeHolder(id);

            }}

          >

            &times;

          </button>

        )}

      </div>

      <div className="line-item-price">${itemPrice}</div>

      <style jsx>{cssHere}</style>

    </div>

  );

};

Holder.propTypes = {

  name: t.string,

  email: t.string,

  itemPrice: t.number,

  id: t.string,

  removeHolder: t.func,

  showRemoveButton: t.bool,

};

export default Holder;

This code uses useState to keep track of the ephemeral form input states for name and email:


const [nameInput, setName] = useState(name);

const [emailInput, setEmail] = useState(email);



You may have noticed that a Redux removeHolder action creator is also putting in props. Combining styles is acceptable.


The local component state has always been OK for situations like this, but before React hooks, I might have been tempted to put it in Redux and get the state from props.


The component state would have required the use of a class component, initial state setting using the class instance property syntax (or a function Object() { [native code] } function), and other additional complexity that was unnecessary in order to avoid Redux. Redux has plug-and-play solutions for managing form state, which made it easier for me to make sure that changing form state didn't affect my business logic.


The decision was easy because I already used Redux in all of my non-trivial apps: Redux (nearly) everything!


The decision is still straightforward as of right now:


Component state for the component state, redux for the application state


When to Use Redux


Another frequent query is, "Should you store everything in Redux?" Will it not compromise time travel debugging if you do not?"


No, because applications have a lot of state that is too granular and transient to offer valuable data for tasks like logging telemetry or time travel debugging. You probably don't need to save every user input and mouse movement in Redux state unless you're developing a real-time collaborative editor. Redux state grows by adding items, which adds another layer of abstraction and the complexity that comes with it.


To put it another way, you should feel free to use Redux, but you should have a good justification for doing so.


In the event that your component:


  • Similar to network or device APIs, uses I/O.

  • State is loaded or saved.

  • State is shared by components that are not children.

  • Handles any shared business logic or data processing with other application components.


Another example from the TDDDay app is like follows:


import React from 'react';

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

import { compose } from 'ramda';

import page from '../../hocs/page.js';

import purchase from './purchase-component.js';

import { addHolder, removeHolder, getHolders } from './purchase-reducer.js';

const PurchasePage = () => {

  // You can use these instead of

  // mapStateToProps and mapDispatchToProps

  const dispatch = useDispatch();

  const holders = useSelector(getHolders);

const props = {

    // Use function composition to compose action creators

    // with dispatch. See "Composing Software" for details.

    addHolder: compose(

      dispatch,

      addHolder

    ),

    removeHolder: compose(

      dispatch,

      removeHolder

    ),

    holders,

  };

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

};

// `page` is a Higher Order Component composed of many

// other higher order components using function composition.

export default page(PurchasePage);



None of the DOM is handled by this component. It is a container component that assigns import presentation component with DOM concerns. Utilizing the React-Redux hooks API, it connects to Redux.


It makes use of Redux since, after the purchase flow is complete, we'll need to save the data to a database. Other elements of the UI depend on the data this form cares about.


It doesn't only care about one part's state; instead, it cares about the state of many parts at once. This state isn't temporary; it lasts through multiple page views or sessions. Local component state won't help with any of these problems unless you build your own state container library on top of the React API, which is much harder than just using Redux.


React's suspense API could one day assist in saving and loading state. To find out if the suspense API can take the place of my Redux saving/loading routines, we'll have to wait until it launches. Without having to simulate I/O services, Redux enables us to neatly isolate side-effects from the rest of the component code. (I favour redux-saga over thunks since it allows for the isolation of effects.) Effect isolation will be a need for React's APIs in order to compete with Redux in this use case.


Final thoughts:

React Hooks are a great addition to the React library, and they give developers powerful tools for making stateful components. React Hooks don't completely replace Redux, but they do give React applications another way to handle state. Even though React Hooks can be used to manage state, Redux is still the best way to manage and store state across an entire application.


How to Use RTK Queries in a React App?

Before we get into the technicality of using RTK Query in place of React query, let's understand the basics and know what actually RTK Query is.


RTK Query is nothing but an advanced tool to fetch data on the client side. It is basically a caching tool and it works similarly to React. The only good thing about RTK Query is that it directly integrates with Redux. When interacting with APIs, developers frequently use Redux with middleware modules like Thunk.


Adding RTK Query to Redux Toolkit is not mandatory, but when you combine both of them in a React project, it brings out the true power of Redux Toolkit. However, it's advisable to use the official Redux+JS template or the Redux+TS template to initialize a new app with React and Redux.


To help you learn, we've created a React App project with all of the code in the src folder. We will provide some best practices to follow when utilizing Redux Toolkit with React in addition to instructing you on how to use RTK queries in React apps.


Install the React-Rtk package:

To get it started, let's start with using the official Redux+JS template or Redux+TS template. It is way easier and faster to create a React app that uses Redux. This also prevents you from making mistakes.


Here are the complete steps to add Redux Toolkit to an old React project:

There are a thousand ways to learn React programming, but if you want to learn it from scratch, then you are on the right platform. Here are detailed instructions for configuring Redux Toolkit and RTK Query with React. You will be able to comprehend the intricacies of the Redux Toolkit.


Let's start with an NPM because most developers are familiar with it. You can also use any other package manager according to availability, as it will not affect the written code.


  1. Launch a New React App

Let's first initialize a new React app if you don't already have one before we begin fetching and installing the necessary dependencies.


Use this command to launch a new TypeScript-enabled React app.



# Redux + Plain JS template

npx create-react-app my-app --template redux


# Redux + TypeScript template

npx create-react-app my-app --template redux-typescript



The project will take a few minutes to get initiated.


  1. Install React-Redux and Redux Toolkit

First of all, you have to install Redux Toolkit and React-redux within the project.


As Redux Toolkit is already installed in the project in Typescript format so we only have to install react-Redux using the below command:



# NPM

npm install @reduxjs/toolkit react-redux

# Yarn

yarn add @reduxjs/toolkit react-redux



React Redux has a dependency on @types/react-redux so the type definition file of the package will be automatically installed with it.


  1. Create Redux Store

Create a redux folder inside the src folder, and within this redux folder, create a store.ts file.


Redux Toolkit is already written in Typescript, so we don’t need to worry about installing its type definition files separately.



import { configureStore } from '@reduxjs/toolkit'


export const store = configureStore({

  reducer: {}

})




There is no need to include configureStore in addition. 


  1. Define Root State

Extract RootState and AppDispatch  and export it directly from the store.ts file.


RootState and AppDispatch inferred from the store will update as you add state slices, API services, or modify middleware settings. 



import { configureStore } from '@reduxjs/toolkit'


export const store = configureStore({

  reducer: {}

})


// Infer the `RootState` and `AppDispatch` types from the store itself

export type RootState = ReturnType<typeof store.getstate>

export type AppDispatch = typeof store.dispatch



  1. Insert Redux Store 

Since the store is created, it must be provided to all application components.


Import ./redux/store and the <Provider> component from react-redux to the index.tsx file.


Wrap the app component in the provider component, then give the provider the store as a prop.



import React from 'react';

import ReactDOM from 'react-dom/client';

import './index.css';

import App from './App';

import reportWebVitals from './reportWebVitals';

// ? Import Provider from react-redux and store from ./redux/store

import { Provider } from 'react-redux';

import { store } from './redux/store';


const root = ReactDOM.createRoot(

  document.getElementById('root') as HTMLElement

);

root.render(

  <React.StrictMode>

    {/* ? Provide the store as prop */}

    <Provider store={store}>

      <App />

    </Provider>

  </React.StrictMode>

);



  1. Define the State Selector

The RootState and AppDispatch types that we defined in the store.ts file can be imported into each component, however, it is advised to create typed versions of the useDispatch and useSelector hooks to be used throughout your entire application. 


  1. Dispatch Typed Hooks

Create a new hooks.ts file within the redux folder ./src/redux.


Write the below code to add the snippets.



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

import type { RootState, AppDispatch } from './store';


// Use throughout your app instead of plain `useDispatch` and `useSelector`

export const useAppDispatch = () => useDispatch<appdispatch>();

export const useAppSelector: TypedUseSelectorHook<rootstate> = useSelector



  1. Create a Redux State Slice

After defining Hooks and State Selector now it’s time to create our first redux state slice.


Create a features directory Inside the redux folder to house all our slices.


Create a new file with the name: src/redux/features/products/authSlice.ts.


Copy and paste the below code in the authSlice.ts file.


import { createSlice, PayloadAction } from '@reduxjs/toolkit';


export interface IUser {

  _id: string;

  name: string;

  email: string;

  photo: string;

  role: string;

  provider?: string;

  active?: boolean;

  verified?: boolean;

  createdAt: Date;

  updatedAt: Date;

  __v: number;

  id: string;

}


interface AuthState {

  user?: IUser | null;

}


const initialState: AuthState = {

  user: null,

};


export const authSlice = createSlice({

  name: 'authSlice',

  initialState,

  reducers: {

    // ? Logout the user by returning the initial state

    logout: () => initialState,

    // Save the user's info

    userInfo: (state, action: PayloadAction<authstate>) => {

      // Redux Toolkit allows us to write "mutating" logic in reducers. It

      // doesn't actually mutate the state because it uses the Immer library,

      // which detects changes to a "draft state" and produces a brand new

      // immutable state based off those changes

      state.user = action.payload.user;

    },

  },

});


export const { logout, userInfo } = authSlice.actions;

// ? Export the authSlice.reducer to be included in the store.

export default authSlice.reducer;




Those who have used Redux code in the past must know that it requires mentioning all state updates immutably by making copies of the state.


Redux Toolkit gives us the luxury of directly mutating any state.


  1. Add Reducer of the Redux State Slice

In the next step, we will import the reducer function.

To do this, we will export the authSlice.ts into the store and add it to the reducers object.


By defining the authUser field inside of the reducer object, we can communicate to the store that the authReducer function should be used to manage any updates to that state.


import { configureStore } from '@reduxjs/toolkit';

// ? import authReducer from authSlice

import authReducer from './features/authSlice'


export const store = configureStore({

  reducer: {

    // ? Add the authReducer to the reducer object

    authUser: authReducer

  },

// ? show the devTools only in development

devTools: process.env.NODE_ENV !== 'production',

});


export type RootState = ReturnType<typeof store.getstate>;

export type AppDispatch = typeof store.dispatch;




  1. Add RTK Query

RTK Query will help us retrieve API data from the cache. It is designed on top of the Redux Toolkit and uses its APIs for implementation.


It is not required to use RTK Query with Redux Toolkit; however, it is highly recommended to combine both Redux Toolkit and RTK Query in your project if you will be managing both local state and API data. While it is not required to use RTK Query with Redux Toolkit, it is highly recommended to do so. 


You are free to make use of other API state management tools such as React Query; however, using Redux Toolkit to get it set up and operational can be a real pain in the rear.


Because of this, the Redux team came to the conclusion that it would be beneficial to develop RTK Query, which has a user interface that is analogous to that of React Query but is more compatible with Redux Toolkit and is less complicated to operate.


  1. Create an API Service

It is necessary to create an API service within the redux directory. 

Create an API file named src/redux/api/products/productAPI.ts


An example of a CRUD operation performed with RTK Query is shown below. In this example, I am retrieving all of the products, obtaining a single product, updating a single product, and deleting a single product.


createAPI will take a single configuration object that may have any of the following parameters if it is provided.


import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/dist/query/react';


type IProduct = {

  _id: string;

  name: string;

  avgRating: number;

  numRating: number;

  price: number;

  description: string;

  countInStock: number;

  quantity?: number;

  imageCover: string;

  images: string[];

  category: string;

  createdAt: Date;

  updatedAt: Date;

  slug: string;

  id: string;

};


export const productApi = createApi({

  reducerPath: 'productApi',

  baseQuery: fetchBaseQuery({ baseUrl: 'http://localhost:8000/api/' }),

  tagTypes: ['Products'],

  endpoints: (builder) => ({

    // ? Query: Get All Products

    getProducts: builder.query<iproduct[], void>({

      query() {

        return 'products';

      },

      providesTags: (result) =>

        result

          ? [

              ...result.map(({ id }) => ({

                type: 'Products' as const,

                id,

              })),

              { type: 'Products', id: 'LIST' },

            ]

          : [{ type: 'Products', id: 'LIST' }],

      // ? Transform the result to prevent nested data

      transformResponse: (response: { data: { products: IProduct[] } }) =>

        response.data.products,

    }),

    // ? Query: Get a single product

    getProduct: builder.query<iproduct, string>({

      query(id) {

        return `products/${id}`;

      },

      transformResponse: (

        response: { data: { product: IProduct } },

        args,

        meta

      ) => response.data.product,

      providesTags: (result, error, id) => [{ type: 'Products', id }],

    }),

    // ? Mutation: Create a product

    createProduct: builder.mutation<iproduct, formdata>({

      query(data) {

        return {

          url: 'products',

          method: 'POST',

          credentials: 'include',

          body: data,

        };

      },

      invalidatesTags: [{ type: 'Products', id: 'LIST' }],

      transformResponse: (response: { data: { product: IProduct } }) =>

        response.data.product,

    }),

    // ? Mutation: Update Product

    updateProduct: builder.mutation<

      IProduct,

      { id: string; formData: FormData }

    >({

      query({ id, formData }) {

        return {

          url: `products/${id}`,

          method: 'PATCH',

          credentials: 'include',

          body: formData,

        };

      },

      invalidatesTags: (result, error, { id }) =>

        result

          ? [

              { type: 'Products', id },

              { type: 'Products', id: 'LIST' },

            ]

          : [{ type: 'Products', id: 'LIST' }],

      transformResponse: (response: { data: { product: IProduct } }) =>

        response.data.product,

    }),

    // ? Mutation: Delete product

    deleteProduct: builder.mutation<null, string>({

      query(id) {

        return {

          url: `products/${id}`,

          method: 'DELETE',

          credentials: 'include',

        };

      },

      invalidatesTags: [{ type: 'Products', id: 'LIST' }],

    }),

  }),

});


export const {

  useCreateProductMutation,

  useUpdateProductMutation,

  useDeleteProductMutation,

  useGetProductsQuery,

  useGetProductQuery,

  usePrefetch,

} = productsApi;



  1. Adding the final API Service to the Redux Store

RTK Query will produce what is known as a "slice reducer" from the productAPI, which is intended to be added to the Redux root reducer.


Additionally, a specialised middleware will be generated, and the middleware parameter will need to be updated to include it.


import { configureStore } from '@reduxjs/toolkit';

// ? import authReducer from authSlice

import authReducer from './features/authSlice';

import { productsApi } from './api/products/productAPI';


export const store = configureStore({

  reducer: {

    // ? Add the authReducer to the reducer object

    authUser: authReducer,

    [productsApi.reducerPath]: productsApi.reducer,

  },

  devTools: process.env.NODE_ENV !== 'production',

   // Adding the api middleware enables caching, invalidation, polling,

   // and other useful features of `rtk-query`.

  middleware: (getDefaultMiddleware) =>

    getDefaultMiddleware({}).concat([productsApi.middleware]),

});


export type RootState = ReturnType<typeof store.getstate>;

export type AppDispatch = typeof store.dispatch;



Final thoughts:

RTK Query implementation in a React app is critical for creating efficient and powerful apps. Developers can make applications that work well and are easy to maintain by using the query syntax and providing the necessary data. Even though the process can be difficult, the end result has many benefits and can help developers make apps with more features that can be used for a wide range of tasks. With the right guidance, developers can easily and effectively implement RTK Query in a React app and take their applications to the next level.


Make new Model/Controller/Migration in Laravel

  In this article, we have included steps to create a model and controller or resource controller with the help of command line(CLI). Here w...