Home > Back-end >  Is it possible use Redux Thunk without Redux?
Is it possible use Redux Thunk without Redux?

Time:07-21

Got this error:

useStore.js:20 Uncaught TypeError: Cannot read properties of null (reading 'store')

In our architecture we have a 1. React / Next.js app and a 2. React based WordPress plugin.

In the Next.js app we uses the classis Redux thunk architecture: https://rangle.io/blog/how-react-and-redux-brought-back-mvc-and-everyone-loved-it/

In plugin as more 'shortcode' can coexist in the same page, there is no Redux. shortcodes communicate - if need - with events through localStorage.

The common code of the two project is in git submodule. We try to use same React components in both project if possible. Wordpress plugin is also React based.

Question: How can a Redux Thunk be called, but at the end not store result in Redux?

i.e. here login happens:

export default function LoginAndRegistrationRAL(props: {
}) {

    return (

dispatch(
    loginEmail({
        method: 'post',
        url: baseURL(env)   'loginEmail',
        data: {
            email: email,
            password: password1,
            tempUserShortId: getTempUserShortId(),
        },
        headers: {
            crossDomain: true,
        },
    })
)

To serve both project need, we store result in local storage and in Redux also:

export const loginEmail = createAsyncThunk(
    `${namespace}/loginEmail`,
    async (req: any, { dispatch }) => {
        const { data } = await axios(req).then((res) => {
            dispatch(persistAuthnUser(res.data))
            return res
        })
        return data
    }
)

so persistAuthnUser stores in localStorage:

export const persistAuthnUser = createAsyncThunk(
    `${namespace}/persistAuthnUser`,
    async (data: any, { dispatch }) => {
        return Promise.resolve().then(function () {
            localStorage.setItem('authnRes', JSON.stringify(data))
        })
    }
)

and after this, result is stored in Redux also:

    extraReducers: (builder) => {
        builder
            .addCase(loginEmail.fulfilled, (state, { payload }) => {
                state.authnRes = payload
            })

How could this part excluded in the Wordpress plugin project?

In Next.js project the initialization of the store happens in _app.tsx:

function MyApp({ Component, pageProps }: AppPropsWithLayout) {

    const getLayout = Component.getLayout ?? ((page) => <Layout>{page}</Layout>)
    return (
        <>
            <Provider store={store}>

This part is missing in Wordpress, as there are no routes, pages in a plugin.

Anyway, can Redux Thunk be used without Redux? Redux Thunk is for async communication, right? Could it exist without Redux?

const slice = createSlice({
    name: 'slice',
    initialState,
    reducers: {

How to prevent this error? Which line raise it?

useStore.js:20 Uncaught TypeError: Cannot read properties of null (reading 'store')

In plugin there is no _app.tsx no <Provider store={store}> call in the main component. It will not call store.ts.

import { configureStore } from '@reduxjs/toolkit'
import sliceReducer from './slice'

export const store = configureStore({
    reducer: { slice: sliceReducer },
    middleware: (getDefaultMiddleware) =>
        getDefaultMiddleware({
            serializableCheck: false,
        }),
})

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

In slice.ts I hae onle this line which link back to store.ts:

import type { RootState } from './store'

Some selectore uses RootState, but it is a type, not a variable. Can it be the reason for error?

Like here, but it is a type here:

export const selectSlice = createSelector(
    [(state: RootState) => state.slice],
    (slice) => slice
)

I found more, so this is the error:

useStore.js:20 Uncaught TypeError: Cannot read properties of null (reading 'store')
    at useStore.js:20:28
    at useDispatch.js:17:17
    at Nn (useAppDispatch.ts:4:37)

In LoginAndRegistrationRAL component useAppDispatch() is used:

const dispatch = useAppDispatch()

useAppDispatch.ts uses AppDispatch

// useAppDispatch.ts

import { useDispatch } from 'react-redux'
import { AppDispatch } from '../store'

export const useAppDispatch = () => useDispatch<AppDispatch>()

Which calls a Redux method:

export function createDispatchHook(context) {
  if (context === void 0) {
    context = ReactReduxContext;
  }

  var useStore = context === ReactReduxContext ? useDefaultStore : createStoreHook(context);
  return function useDispatch() {
    var store = useStore();
    return store.dispatch;
  };
}

Does it mean dispatching a thunk relies on the Redux store behind? And if store does not exist, because _app.tsx does no exist I can not dispatch thunk?

CodePudding user response:

How can a Redux Thunk be called, but at the end not store result in Redux?

They are action creators by nature, the middleware integrates them with the store, I'd strongly advise against bending the library to your will this way. Any developer stumbling upon this monstrosity will pull their hair out!

Redux Thunk is for async communication, right? Could it exist without Redux?

Thunk is for async operation, not exclusively for talking to a backend via AJAX. However, this is what your two use cases seem to have in common: Sending HTTP requests to the same backend. Therefore, I'd recommend to refactor code into a thin API client that does exactly this, regardless of any other libraries in the stack. For example your login thunk would call the following api client:

// apiClient.ts

export const login = (email, password): Promise<AuthResponse> => {
  // Do everything HTTP-request-related in here!
  // headers, getTempUserShortId(), etc.

  return axios.post({ ... });
};

And the wordpress plugin stuff would do the same. Don't try to make thunks work in an environment without redux, reuse the logic that is actually shared.

We try to use same React components in both project if possible. Wordpress plugin is also React based.

If you need to re-use entire React components in different environments (one with redux, one without), the connect() API from redux is your friend. You can build "dumb" components that only receive props to do API requests, and then import those in different React projects. The redux project would use the connected versions of the components and pass your thunks to them via mapDispatchToProps, the wordpress thing would require parent components managing the state that the dumb components use.

  • Related