React Data Fetching Pattern - Part III
Tags: react, redux, api
Published: 2022-11-16 16:31:16

This article is a continuation of 
* [React Data Fetching Patterns - useState-useEffect](https://dev.to/francisldn/react-data-fetching-patterns-40mg)
* [React Data Fetching Pattern - Custom Hook using Context API](https://dev.to/francisldn/react-data-fetching-patterns-part-ii-10gg)

Click [here](https://github.com/francisldn/React-Data-Fetching-Patterns) to access the Github repo for the code examples discussed below.

<h3>Redux createAsyncThunk </h3>
While React Context API is suitable for smaller apps, we need a more performant solution for larger apps. 

**Redux** provides a solution to the problem of unnecessary re-rendering of components posed by Context API, as Redux only re-renders the updated component. Within the ``Redux-toolkit`` library, one has access to an API known as ``createAsyncThunk``.

``createAsyncThunk`` will accept a callback function that returns a promise - this is where the ``getUsers``API function will reside. Upon execution of the ``createAsyncThunk`` function, similar to a Promise function, it will generate 3 types of actions: *pending*, *fulfilled*, *rejected*, and the ``users`` state will be updated accordingly depending on the action type.
Below is an example of the code:

```
interface UsersDataState {
  users: User[]
  loading: boolean
  error: string
}

const initialState: UsersDataState = {
  users: [] as User[],
  loading: false,
  error: "",
}

// set up createAsyncThunk function
export const getUsersData = createAsyncThunk("users/fetchUsers", () => {
  return getUsers(apiURL).then(({results}) => results)
})
```
```
const userSlice = createSlice({
  name: "user",
  initialState,
  reducers: {},

  extraReducers: (builder) => {
// Pending - users state is not updated
    builder.addCase(getUsersData.pending, (state) => {
      state.loading = true
    })

// Fulfilled - users state is updated
    builder.addCase(getUsersData.fulfilled, (state, action: PayloadAction<UserApi[]>) => {
      state.users = action.payload
        .map((user: UserApi) => {
          return {
            id: user.id.value,
            firstName: user.name.first,
            lastName: user.name.last,
            username: user.login.username,
            email: user.email,
          }
        })

      state.loading = false
    })
    
// Rejected - users state is an empty array
    builder.addCase(getUsersData.rejected, (state, action) => {
      state.loading = false
      state.users = []
      state.error = action.error.message || "Something went wrong"
    })
  },
})
```
For a React component to have access to the ``users`` state using Redux, one also has to set up a **redux store** and then wrap the ``<App />`` component with ``Redux Provider``. You may refer to the boilerplate code in the [repo here](https://github.com/francisldn/React-Data-Fetching-Patterns) for further details.

Having done the above, let's see how we can retrieve the data in the component for UI rendering.

As shown below, within a React component, one has access to 2 redux hooks:
* ``useAppSelector`` - enable access to states
* ``useAppDispatch`` - enable update to states

We can use ``useEffect`` hook to trigger the data fetching and updating of the ``users`` state. Then the state will be used for UI rendering.

```
export default function CardList() {
    const {users, loading, error} = useAppSelector((state) => state.user)
    const dispatch = useAppDispatch()

    // update of users state with useEffect
    useEffect(() => {
        dispatch(getUsersData())
    },[])

  return (
    <div className="grid sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 justify-center grid-cols-1 mt-8">
        {loading 
        ? <Loading />
        : error 
        ? <ErrorFallBack />
        : users.map((user:User) => (<CardItem key={user.id} user={user}/>))
        }
    </div>
  )
}
```
<h4>Pros</h4>
* *More Performant than Context API*: As Redux avoids unnecessary re-rendering, the app is more performant as a result.

<h4>Cons</h4>
* *Complicated setup*: As demonstrated above, there are a lot of boilerplate codes to set up redux slicer and store. 

Having discussed a few data fetching methods, could there be a solution that is both simple to set up and performant? Let's proceed to look at the next method - **SWR**.

To be continued:
* [SWR](https://dev.to/francisldn/react-data-fetching-pattern-part-iv-3mbe)
* [React Query](https://dev.to/francisldn/react-data-fetching-pattern-part-v-2oen)

For more information on Redux ``createAsyncThunk``, see [here](https://redux-toolkit.js.org/api/createAsyncThunk).

***If you like the content, please hit the like button so that it can reach more people.***