Skip to content

Redux store

Jimmy edited this page Mar 22, 2023 · 1 revision

Redux Store

This boilerplate use Redux-Toolkit and RTKQuery to deal with business side. We use them because they are often used by the community, very trendy and easy to debug.

RTKQuery is a powerful data fetching and caching tool. So we using it for asynchronous api calls.

Redux-Toolkit is intended to be the standard way to write Redux logic. So we using it for synchronous operations.

How is it used in this boilerplate ❓

For the RTKQuery side, all is located in Services. You will find api.js file that contains the declaration of the fetchBaseQuery customized with an interceptor and the createApi with the fetchBaseQuery and empty endpoints.

    import { Config } from '@/Config'
    import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react'
    
    const baseQuery = fetchBaseQuery({ baseUrl: Config.API_URL })
    
    const baseQueryWithInterceptor = async (args, api, extraOptions) => {
      let result = await baseQuery(args, api, extraOptions)
      if (result.error && result.error.status === 401) {
        // here you can deal with 401 error
      }
      return result
    }
    
    export const api = createApi({
      baseQuery: baseQueryWithInterceptor,
      endpoints: () => ({}),
    })

Next to the Services/api.js file you have a modules folder. Each module corresponds to an entity type and will inject endpoints into the exported api const of Services/api.js

For example , here are the user services :

    import { api } from '../../api'
    import fetchOne from './fetchOne'
    
    export const userApi = api.injectEndpoints({
      endpoints: build => ({
        fetchOne: fetchOne(build), // Code split of the service api call
        // You can add endpoints here
      }),
      overrideExisting: false,
    })
    
    export const { useLazyFetchOneQuery } = userApi
    //                    |-- generated query which will be used in Containers
    export default build => build.query({
        query: id => `/users/${id}`,
    })

Then in your containers it could be use like this :

import React, { useState, useEffect } from 'react'
import { View, TextInput } from 'react-native'
import { useLazyFetchOneQuery } from '@/Services/modules/users' // Import the query

const ExampleContainer = () => {
  const [userId, setUserId] = useState('9')
  
  const [
    fetchOne,
    { data, isSuccess, isLoading, isFetching, error },
  ] = useLazyFetchOneQuery() // use the query

  useEffect(() => {
    fetchOne(userId)
  }, [fetchOne, userId])

  return (
    <View>
      <TextInput
        onChangeText={setUserId}
        editable={!isLoading}
        keyboardType={'number-pad'}
        maxLength={1}
        value={userId}
      />
    </View>
  )
}

export default ExampleContainer

The RTKQuery is linked to the redux store in order to make it work and be debuggable with Flipper.

:::info See the API usage for more information :::

On the redux-toolkit side, we use it to configure all the store and save the default theme of the application.

For example, the storing of the favorite theme of the user

import { createSlice } from '@reduxjs/toolkit'

const slice = createSlice({
  name: 'theme',
  initialState: { theme: null, darkMode: null },
  reducers: {
    changeTheme: (state, { payload: { theme, darkMode } }) => {
      if (typeof theme !== 'undefined') {
        state.theme = theme
      }
      if (typeof darkMode !== 'undefined') {
        state.darkMode = darkMode
      }
    },
    setDefaultTheme: (state, { payload: { theme, darkMode } }) => {
      if (!state.theme) {
        state.theme = theme
        state.darkMode = darkMode
      }
    },
  },
})

export const { changeTheme, setDefaultTheme } = slice.actions
//                  |-------------|-- Generated actions which will be used in Containers

export default slice.reducer

:::info See the API usage for more information :::

Clone this wiki locally