简体   繁体   English

无法访问 Typed React Redux 存储上的属性

[英]Unable to access properties on Typed React Redux store

I been searching up and down google, official docs, stack overflow for a way to access my Redux store, from a React functional component (Typescript).我一直在搜索谷歌、官方文档、堆栈溢出,以寻找一种从 React 功能组件(Typescript)访问我的 Redux 存储的方法。 In theory this should be easy to do with the various guides out there, but no matter what I do I keep getting undefined errors.从理论上讲,使用各种指南应该很容易做到这一点,但无论我做什么,我都会收到未定义的错误。 I am able to get the store object with a custom typed selector hook and console.log it and see the properties as expected, but when accessing them, I am still getting access denied.我能够使用自定义类型的选择器挂钩和 console.log 获取商店对象并按预期查看属性,但是在访问它们时,我仍然拒绝访问。 Strangely enough, the only way I was able to get this to work was stringifying and then parsing the store object as JSON.奇怪的是,我能够让它工作的唯一方法是字符串化,然后将存储对象解析为 JSON。 While that did work that is not ideal and I trying to figure out the proper way to achieve this.虽然这样做并不理想,但我试图找出实现这一目标的正确方法。 I believe I have narrowed it down to some Typing issues.我相信我已经将范围缩小到一些打字问题。 I should also note that I have no issues dispatching actions to update the values in the store.我还应该注意,我在调度操作以更新商店中的值时没有问题。 I may not be explaining my scenario well, so here is my code and examples to better demonstrate:我可能没有很好地解释我的场景,所以这里是我的代码和示例,以更好地演示:

Setup设置

/src/state/action-types/index.ts: /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: /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: /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: /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: /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: /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: /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 src/hooks/useTypedSelector.ts

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

export const useTypedSelector: TypedUseSelectorHook<RootState> = useSelector

Examples例子

Ok this is where the fun begins.好的,这就是乐趣开始的地方。

If I do this: const stateA = useTypedSelector((state) => state) I get the overall store object in the console.log:如果我这样做: const stateA = useTypedSelector((state) => state)我会在 console.log 中获得整个存储对象:

{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

But if I try to do: const stateA = useTypedSelector((state) => state.appLaunchCount) I get this error, even though the output is logged properly .但是,如果我尝试这样做: const stateA = useTypedSelector((state) => state.appLaunchCount)我收到此错误,即使输出已正确记录

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

I was still getting an output logged on the store object, I had an the idea to stringify and parse that state object, and then I was able to access the properties: const stateB = JSON.parse( useTypedSelector(( state ) => JSON.stringify( state )))我仍然在 store 对象上记录了一个输出,我有一个想法来字符串化和解析那个状态对象,然后我能够访问属性: const stateB = JSON.parse( useTypedSelector(( state ) => JSON.stringify( state )))

However, the documentation I find online says should be able to access properties like this: const stateC= useTypedSelector((state) => state.appstate.appLaunchCount) , but instead I get this error:但是,我在网上找到的文档说应该能够访问这样的属性: const stateC= useTypedSelector((state) => state.appstate.appLaunchCount) ,但我收到了这个错误:

Uncaught TypeError: Cannot read property 'appLaunchCount' of undefined

I suspect it may be an issue with the shape or Type of the store, but I am not sure what else I can try.我怀疑这可能是商店的形状或类型的问题,但我不确定我还能尝试什么。 Last clue I have is that if I hover over the RootState object is this:我的最后一条线索是,如果我将鼠标悬停在 RootState 对象上,则是这样的:

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

Not sure that empty object is about and/or if it preventing me from accessing the properties.不确定空对象是关于和/或是否阻止我访问属性。 Please assist.请协助。

You have a mismatch in your reducer file and your store setup file.您的减速器文件和商店设置文件不匹配。

In src/state/index.ts: , you have: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>

and in src/state/store.ts , you have:src/state/store.ts ,你有:

import { reducer } from './reducers'
import { composeWithDevTools } from 'redux-devtools-extension'

export const store = createStore(
    reducer,
    // etc
)

If you look very carefully... you imported reducer into your store file.如果你仔细看......你进口reducer到您存储文件。 That's the individual "app state" slice reducer, not your combined "root reducer".那是单独的“应用程序状态”切片减速器,而不是您组合的“根减速器”。 But, the TS type you're exporting is the combined root reducer.但是,您要导出的 TS 类型组合的根减速器。

So, you set up the types correctly, but got the runtime behavior doing something else.因此,您正确设置了类型,但运行时行为却在做其他事情。

Change import { reducer } to import { reducers } , and fix what you're passing to the store to match, and it should work okay. import { reducer }更改为import { reducers } ,并修复您传递给商店的内容以匹配,它应该可以正常工作。

As a side note: you should really be usingour official Redux Toolkit package and following the rest of our TS setup guidelines .附带说明:您确实应该使用我们的官方 Redux Toolkit 包并遵循我们的 TS 设置指南的其余部分 That would completely eliminate all the "actions" files, part of the code in the reducer, and the rest of the config setup in the store file.这将完全消除所有“操作”文件、reducer 中的部分代码以及商店文件中的其余配置设置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM