【问题标题】:Unable to access properties on Typed React Redux store无法访问 Typed React Redux 存储上的属性
【发布时间】:2021-07-21 20:37:20
【问题描述】:

我一直在谷歌、官方文档、堆栈溢出中搜索从 React 功能组件(Typescript)访问我的 Redux 存储的方法。从理论上讲,使用各种指南应该很容易做到这一点,但无论我做什么,我都会不断收到未定义的错误。我可以使用自定义类型的选择器钩子和 console.log 获取 store 对象并按预期查看属性,但是在访问它们时,我仍然被拒绝访问。奇怪的是,我能够让它工作的唯一方法是字符串化,然后将存储对象解析为 JSON。虽然这样做并不理想,但我试图找出实现这一目标的正确方法。我相信我已将其范围缩小到一些打字问题。我还应该注意,我在调度操作以更新商店中的值时没有问题。我可能没有很好地解释我的场景,所以这里是我的代码和示例以更好地演示:

设置

/src/state/action-types/index.ts:

export enum ActionType {
    UPDATE_LOADING_STATUS = 'update_loading_status',
    UPDATE_ONLINE_STATUS = 'update_online_status',
    UPDATE_APP_LAUNCH_COUNT = 'update_app_launch_count',
}

/src/state/actions/index.ts:

import { ActionType } from '../action-types'

export interface UpdateLoadingStatus {
    type: ActionType.UPDATE_LOADING_STATUS
    payload: boolean
}

export interface UpdateOnlineStatus {
    type: ActionType.UPDATE_ONLINE_STATUS
    payload: boolean
}

export interface UpdateAppLaunchCount {
    type: ActionType.UPDATE_APP_LAUNCH_COUNT
}

export type Action = UpdateLoadingStatus | UpdateOnlineStatus | UpdateAppLaunchCount

/src/state/reducers/AppStateReducer.ts:

import produce from 'immer'
import { ActionType } from '../action-types'
import { Action } from '../actions'

interface AppState {
    isLoading: boolean
    isOnline: boolean
    isAppVisible: boolean | null
    entitlements: string[] | null
    persona: string | null
    theme: 'light' | 'dark' | 'default'
    appLaunchCount: number
}

const initialState: AppState = {
    isLoading: true,
    isOnline: false,
    isAppVisible: null,
    entitlements: null,
    persona: null,
    theme: 'default',
    appLaunchCount: 0,
}

export const reducer = produce((state: AppState = initialState, action: Action) => {
    switch (action.type) {
        case ActionType.UPDATE_LOADING_STATUS:
            state.isLoading = action.payload
            return state
        case ActionType.UPDATE_ONLINE_STATUS:
            state.isOnline = action.payload
            return state
        case ActionType.UPDATE_APP_LAUNCH_COUNT:
            state.appLaunchCount = state.appLaunchCount + 1
            return state
        default:
            return state
    }
}, initialState)

/src/state/index.ts:

import { combineReducers } from 'redux'
import { reducer as AppStateReducer } from './reducers/AppStateReducer'

export const reducers = combineReducers({
    appstate: AppStateReducer,
})

export type RootState = ReturnType<typeof reducers>

/src/state/store.ts:

import { createStore, applyMiddleware } from 'redux'
import thunk from 'redux-thunk'
import { reducer } from './reducers'
import { composeWithDevTools } from 'redux-devtools-extension'

export const store = createStore(
    reducer,
    {
        isLoading: true,
        isOnline: false,
        isAppVisible: null,
        entitlements: null,
        persona: null,
        theme: 'default',
        appLaunchCount: 0,
    },
    composeWithDevTools(applyMiddleware(thunk))
)

/src/index.tsx:

import * as ReactDom from 'react-dom'
import { Provider } from 'react-redux'
import { store } from './state/store'
import { App } from './components/App'

ReactDom.render(
    <Provider store={store}>
        <App />
    </Provider>,
    document.querySelector('#root')
)

/src/components/App.tsx:

import { useEffect } from 'react'
import { useActions } from '../hooks/useActions'
import { useTypedSelector } from '../hooks/useTypedSelector'
import { RootState } from '../state'

export const App: React.FC = () => {
    const { updateLoadingStatus, updateOnlineStatus, updateAppLaunchCount } = useActions()

    const stateA = useTypedSelector((state) => state)

    console.log(state)

    return (
        ...content...
    )
}

src/hooks/useTypedSelector.ts

import { useSelector, TypedUseSelectorHook } from 'react-redux'
import { RootState } from '../state'

export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector

示例

好的,这就是乐趣的开始。

如果我这样做: const stateA = useTypedSelector((state) =&gt; state) 我在 console.log 中得到了整个 store 对象:

{isLoading: false, isOnline: true, isAppVisible: null, entitlements: null, persona: null, …}
appLaunchCount: 2
entitlements: null
isAppVisible: null
isLoading: false
isOnline: true
persona: null
theme: "default"
__proto__: Object

但如果我尝试这样做: const stateA = useTypedSelector((state) =&gt; state.appLaunchCount) 我得到了这个错误,即使输出被正确记录

Property 'appLaunchCount' does not exist on type 'CombinedState<{ appstate: AppState; }>'

我仍然在 store 对象上记录输出,我有一个想法来字符串化和解析该状态对象,然后我能够访问属性: const stateB = JSON.parse( useTypedSelector(( state ) =&gt; JSON.stringify( state )))

但是,我在网上找到的文档说应该能够访问这样的属性: const stateC= useTypedSelector((state) =&gt; state.appstate.appLaunchCount),但我得到了这个错误:

Uncaught TypeError: Cannot read property 'appLaunchCount' of undefined

我怀疑这可能是商店的形状或类型有问题,但我不确定我还能尝试什么。我得到的最后一条线索是,如果我将鼠标悬停在 RootState 对象上是这样的:

(alias) type RootState = EmptyObject & {
    appstate: AppState;
}

不确定空对象是关于和/或它是否阻止我访问属性。请帮忙。

【问题讨论】:

    标签: javascript reactjs typescript redux


    【解决方案1】:

    您的 reducer 文件和商店设置文件不匹配。

    src/state/index.ts:,你有:

    import { combineReducers } from 'redux'
    import { reducer as AppStateReducer } from './reducers/AppStateReducer'
    
    export const reducers = combineReducers({
        appstate: AppStateReducer,
    })
    
    export type RootState = ReturnType<typeof reducers>
    

    src/state/store.ts 中,您有:

    import { reducer } from './reducers'
    import { composeWithDevTools } from 'redux-devtools-extension'
    
    export const store = createStore(
        reducer,
        // etc
    )
    

    如果您非常仔细查看...您已将 reducer 导入到您的商店文件中。那是单独的“应用程序状态”切片缩减器,不是您的组合“根缩减器”。但是,您要导出的 TS 类型组合的根减速器。

    因此,您正确设置了类型,但运行时行为却做了其他事情。

    import { reducer } 更改为import { reducers },并修复您传递给商店的内容以进行匹配,它应该可以正常工作。

    附带说明:您确实应该使用our official Redux Toolkit package 并关注the rest of our TS setup guidelines。这将完全消除所有“操作”文件、reducer 中的部分代码以及 store 文件中的其余配置设置。

    【讨论】:

    • 哇,成功了。那是确切的问题,我将要传递给商店的对象的导入和形状更新为 { appState: {params}} 以清除它。感谢您易于理解的解释。我也会考虑使用官方工具包进行重构:)
    猜你喜欢
    • 1970-01-01
    • 2017-02-27
    • 2021-08-23
    • 1970-01-01
    • 2021-06-06
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多