reduxでTypeError: _useSelector is undefinedと言われた時


ある日

お勉強用react製web appをいじっていたら TypeError: _useSelector is undefined というエラーが!

しかし普通にbuildは通る そしてググっても出てこない(気がする)

↓ばーじょん↓

こんなコード書いてた

// src/containers/App.tsx
const appSelector = createSelector(
  (state: { init: AppState }) => state.init,
  init => init
)

export const AppContainer: React.FC = () => {
  const dispatch = useDispatch()
  const { loading, loaded, error } = useSelector(appSelector)
  const loadDispatcher = (): void => {
    dispatch(initOp())
  }
  return (
    <AppView
      loading={loading}
      loaded={loaded}
      error={error}
      loadDispatcher={loadDispatcher}
    />
  )
}

// src/reducers/index.ts
export default combineReducers({
  app: appReducer,
  todos: todoReducer,
  visibilityFilter: visibilityFilterReducer,
})

// src/reducers/app.ts
export const appReducer = reducerWithInitialState({
  loaded: false,
  loading: false,
  error: null,
} as AppState)
  .cases(...

と、こうやって書けば一目瞭然で(実際はファイルが別れているのでそこそこ時間を取られた)

  • appSelector()ではinitからAppStateを取得しようとしている
  • combineReducers()ではappappReducerとして結合されている

このため、上記エラーが出たようだった。

実際にはuseSelector()の引数にダミーの(() => true的な)selectorを使うなどしてエラー原因の特定を行った。

以上よりappSelectorを以下のように修正することでエラーは解消した。

// reselect使っている理由は特に無いです(使ってみたかった)
// この程度ならワンライナーで
// const appSelector = ({ app }: { app: AppState }): AppState => app
// の方が分かりやすいですね
const appSelector = createSelector(
  (state: { app: AppState }) => state.app,
  app => app
)

所感

reducerが無いと_useSelector()が未定義状態になるのかーというのが一番の収穫

当初InitStateとして定義していたが名前的に微妙だな思いAppStateに変えたのだが、この修正が一部漏れていた

selector周りでtypescriptの型チェックが微妙な感じになってしまうのは予想外で、もう少し丁寧に書くべきだったと反省