Search code examples
reactjsredux-toolkit

Redux Toolkit - How useSelector is referring to initialState through reducer?


I'm trying to understand (ineffectively) how does useSelector refers to initialState stored in Slice with the line const count = useSelector((state) => state.counter.value)

Referring to documentation, where this is Slice:

import { createSlice } from '@reduxjs/toolkit'

const initialState = {
  value: 0,
}

export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  reducers: {
    increment: (state) => {
      // 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.value += 1
    },
    decrement: (state) => {
      state.value -= 1
    },
    incrementByAmount: (state, action) => {
      state.value += action.payload
    },
  },
})

// Action creators are generated for each case reducer function
export const { increment, decrement, incrementByAmount } = counterSlice.actions

export default counterSlice.reducer

And this is store:

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export const store = configureStore({
  reducer: {
    counter: counterReducer,
  },
})

In Component Counter I'm trying to access the initialState:

...
export function Counter() {
  const count = useSelector((state) => state.counter.value)
  const dispatch = useDispatch()

  return (
    <div>
     ...
    </div>
  )
}

so how does const count = useSelector((state) => state.counter.value) refer to initialState of slice if in counterSlice by default I export only reducer object?

export default counterSlice.reducer


Solution

  • The exported reducer object is not the same as the reducers property you provide to createSlice. The function createSlice does a lot of magic behind the scene that you can read more in RTK's source here: createSlice and createReducer.

    TL;DR: createSlice passes the provided initialState to a function createReducer like this:

    createReducer(options.initialState, (builder) => {
      // Some code
    }
    

    And then createReducer attaches the initialState to the reduce like this:

    if (isStateFunction(initialState)) {
      getInitialState = () => freezeDraftable(initialState())
    } else {
      const frozenInitialState = freezeDraftable(initialState)
      getInitialState = () => frozenInitialState
    }
    
    // Some code
    
    reducer.getInitialState = getInitialState
    

    With that, the store can get the initialState.