question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Jest: TypeError: Cannot read properties of undefined (reading 'reducerPath')

See original GitHub issue

Got the following error. Screenshot 2022-07-01 at 15 40 23 in test

it('should render form fields', async () => {
    renderWithProviders(<FormBuilder {...props} />);
});
export const renderWithProviders = (element: React.ReactElement) => {
  return renderer.create(<Provider store={store}>{element}</Provider>);
};

FormBuilder:

import React, { useEffect } from 'react';
import { View } from 'react-native';
import { Formik, useFormikContext } from 'formik';

import { Button } from '@/Components/Buttons/Button';
import { Checkbox, CheckboxProps } from '@/Components/Controls/Checkbox';
import { OwnAccountSelector, OwnAccountSelectorProps } from '@/Components/OwnAccountSelector';
import {
  AccountSelectorFieldValues,
  CheckboxFieldValues,
  FormBuilderProps,
  InputFieldValues,
  FieldValues,
  FieldTypes,
  BoxInputFormProps,
  SelectorFiledValues,
  BaseInputFormProps,
  BaseInputSubtypes,
  BoxInputSubtypes,
  MobileOperatorFieldValues,
} from './types';
import {
  MobileOperatorCarousel,
  MobileOperatorCarouselProps,
} from '@/Components/Carousels/MobileOperatorCarousel';
import { SelectPicker } from '@/Components/SelectPicker';
import { MobileOperator } from '@/Types/MobileOperator';
import { useInputFocus } from '@/Hooks/input';
import { BankAndAccountInput, BankAndAccountValue } from '../Inputs/BankAndAccountInput';
import { DatePicker } from '../Inputs/DatePicker';
import { BoxInputForm } from './BoxInputForm';
import { BaseInputForm } from './BaseInputForm';
import { BaseInputProps } from '../Inputs/BaseInputs';
import { SelectPickerProps } from '../SelectPicker/types';
import { useInstitutionColors } from '@/Hooks/institutionColors';

const FormValuesGrabber = ({
  onValuesChange,
}: {
  onValuesChange?: (values: any, submitForm: any) => void;
}) => {
  const { values, submitForm } = useFormikContext<FieldValues>();
  useEffect(() => {
    onValuesChange && onValuesChange(values, submitForm);
  }, [values, onValuesChange, submitForm]);
  return null;
};

export function FormBuilder<ReturnType extends FieldValues>({
  formRef,
  fields,
  initialValues = {} as ReturnType,
  onSubmit,
  onValuesChange,
  validationSchema,
  submitButtonText = 'Submit',
  submitButtonStyle = {},
  canProceed = true,
  hideSubmit = false,
}: FormBuilderProps<ReturnType>) {
  const { focusState, setFieldFocus, resetFieldFocus, resetFocusAll } = useInputFocus(fields);
  const institutionColors = useInstitutionColors();

  return (
    <Formik
      innerRef={formRef}
      initialValues={initialValues}
      validateOnMount
      onSubmit={(values) => {
        resetFocusAll();
        onSubmit(values as ReturnType);
      }}
      validationSchema={validationSchema}
    >
      {({ handleSubmit, setFieldValue, setFieldTouched, values, errors, isValid, touched }) => (
        <>
          <FormValuesGrabber onValuesChange={onValuesChange} />
          {fields.map((field) => {
            if (!field.hidden) {
              const {
                name,
                type,
                subtype,
                fieldProps = {},
                additionalComponent,
                additionalComponentProps = {},
              } = field;
              const AdditionalComponent = additionalComponent;
              let fieldComponent = <></>;

              const commonInputProps = {
                isFocused: focusState[name],
                onFocus: () => setFieldFocus(name),
                onBlur: () => {
                  resetFieldFocus(name);
                  setFieldTouched(name);
                },
                error: touched[name] && errors[name] ? (errors[name] as string) : undefined,
              };

              switch (type) {
                case FieldTypes.BASE_INPUT:
                  fieldComponent = (
                    <BaseInputForm
                      {...commonInputProps}
                      {...(fieldProps as BaseInputFormProps)}
                      subtype={subtype as BaseInputSubtypes}
                      value={(values as InputFieldValues)[name]}
                      onChangeText={(value) => setFieldValue(name, value)}
                    />
                  );
                  break;

                case FieldTypes.BOX_INPUT:
                  fieldComponent = (
                    <BoxInputForm
                      {...(fieldProps as BoxInputFormProps)}
                      {...commonInputProps}
                      subtype={subtype as BoxInputSubtypes}
                      value={(values as InputFieldValues)[name]}
                      onChangeText={(value) => setFieldValue(name, value)}
                    />
                  );
                  break;

                case FieldTypes.CHECKBOX:
                  fieldComponent = (
                    <Checkbox
                      {...(fieldProps as CheckboxProps)}
                      isActive={(values as CheckboxFieldValues)[name]}
                      onPress={() => setFieldValue(name, !values[name])}
                    />
                  );
                  break;

                case FieldTypes.OWN_ACCOUNT_SELECTOR:
                  fieldComponent = (
                    <OwnAccountSelector
                      {...(fieldProps as OwnAccountSelectorProps)}
                      selectedAccount={(values as AccountSelectorFieldValues)[name]}
                      onChangeSelected={(account) => setFieldValue(name, account)}
                    />
                  );
                  break;

                case FieldTypes.SELECTOR:
                  fieldComponent = (
                    <SelectPicker
                      {...(fieldProps as SelectPickerProps<any>)}
                      onPress={() => setFieldTouched(name)}
                      error={commonInputProps.error}
                      selectedValue={(values as SelectorFiledValues)[name]}
                      onChangeSelected={(item: any) => setFieldValue(name, item)}
                    />
                  );
                  break;

                case FieldTypes.DATE:
                  fieldComponent = (
                    <DatePicker
                      {...(fieldProps as BaseInputProps)}
                      error={commonInputProps.error}
                      onClose={() => setFieldTouched(name)}
                      value={(values as InputFieldValues)[name]}
                      onChangeDate={(date) => setFieldValue(name, date)}
                    />
                  );
                  break;

                case FieldTypes.MOBILE_OPERATOR_CAROUSEL:
                  fieldComponent = (
                    <MobileOperatorCarousel
                      {...(fieldProps as MobileOperatorCarouselProps)}
                      value={(values as MobileOperatorFieldValues)[name]}
                      onChangeSelected={(item: MobileOperator) => setFieldValue(name, item)}
                    />
                  );
                  break;

                case FieldTypes.BANK_AND_ACCOUNT:
                  fieldComponent = (
                    <BankAndAccountInput
                      isFocused={commonInputProps.isFocused}
                      onBlur={commonInputProps.onBlur}
                      onFocus={commonInputProps.onFocus}
                      error={commonInputProps.error as {}}
                      value={values[name] as BankAndAccountValue}
                      setFieldValue={(value) => setFieldValue(name, value)}
                    />
                  );
                  break;

                default:
                  break;
              }

              return (
                <View key={name}>
                  {fieldComponent}
                  {AdditionalComponent && (
                    <AdditionalComponent value={values[name]} {...additionalComponentProps} />
                  )}
                </View>
              );
            }
          })}

          {!hideSubmit && (
            <Button
              onPress={handleSubmit}
              disabled={!isValid || !canProceed}
              title={submitButtonText}
              style={submitButtonStyle}
              color={institutionColors.primary}
            />
          )}
        </>
      )}
    </Formik>
  );
}

Store:

import EncryptedStorage from 'react-native-encrypted-storage';
import { combineReducers } from 'redux';
import {
  persistReducer,
  persistStore,
  FLUSH,
  REHYDRATE,
  PAUSE,
  PERSIST,
  PURGE,
  REGISTER,
} from 'redux-persist';
import { configureStore, isRejectedWithValue, Middleware, MiddlewareAPI } from '@reduxjs/toolkit';
import { navigate } from '@/Navigators/Root';
// Services
import { rtkQueryService } from '@/Services/api';
// State
import commonSlice, { addError, clearError } from '@/Store/Common/commonSlice';
import profileSlice from '@/Store/Profile/profileSlice';
import userSlice from '@/Store/User/userSlice';
import catalogsSlice from '@/Store/Catalogs/catalogsSlice';
import startup from '@/Store/Startup';
import i18n from '@/Translations';

const rtkQueryErrorLogger: Middleware = (api: MiddlewareAPI) => (next) => (action) => {
  if (isRejectedWithValue(action)) {
    let errorMessage = i18n.t('errorLogger.defaultError');

    if (action?.payload?.error) {
      errorMessage = action?.payload?.error.replace('Error: ', '').toLowerCase();
      action.payload.error = errorMessage;
    }

    api.dispatch(addError(errorMessage));

    const { common } = api.getState() as RootState;

    if (!common.pendingRequests.length && common.asyncError) {
      navigate('PopUpModal', {
        icon: 'error',
        title: i18n.t('errorLogger.modalTitle'),
        text: common.asyncError,
      });

      api.dispatch(clearError());
    }
  }

  return next(action);
};

const reducers = combineReducers({
  // State
  // When adding new slice add clear slice to clearUserData() except persistConfig
  startup,
  common: commonSlice,
  profile: profileSlice,
  catalogs: catalogsSlice,
  user: userSlice,
  // Services
  // When adding new service add it to services array inside clearRTKQueryCache()
  [rtkQueryService.reducerPath]: rtkQueryService.reducer,
});

const persistConfig = {
  key: 'root',
  storage: EncryptedStorage,
  whitelist: ['profile'],
};

const persistedReducer = persistReducer(persistConfig, reducers);

const store = configureStore({
  reducer: persistedReducer,
  middleware: (getDefaultMiddleware) => {
    const middlewares = getDefaultMiddleware({
      serializableCheck: {
        ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
      },
    }).concat([rtkQueryErrorLogger, rtkQueryService.middleware]);

    if (__DEV__ && !process.env.JEST_WORKER_ID) {
      const createDebugger = require('redux-flipper').default;
      middlewares.push(createDebugger());
    }

    return middlewares;
  },
});

const persistor = persistStore(store);

export { store, persistor };

export type RootState = ReturnType<typeof store.getState>;

export type AppDispatch = typeof store.dispatch;

Issue Analytics

  • State:closed
  • Created a year ago
  • Reactions:1
  • Comments:16 (4 by maintainers)

github_iconTop GitHub Comments

6reactions
phryneascommented, Jul 3, 2022

Having 0, 1 or one million endpoints is not your problem. Your rtkQueryService is getting imported as undefined.

There are two possible reasons for this:

  • your jest generally deals with imports an export incorrectly
  • you have a circular import that accidentally is resolved correctly when you use your bundler, but not when you use jest. This is the more likely option.

Look for an import circle where the file with rtkQueryService imports from a file that also imports rtkQueryService.

1reaction
fregayegcommented, Jul 3, 2022

Hello! Here is my createApi()

export const rtkQueryService = createApi({
  tagTypes: ['accountBalance'],
  reducerPath: 'rtkQueryService',
  baseQuery: baseQueryWithInterceptor,
  endpoints: () => ({}),
});

And then I inject endpoints (in the separate files):

export const beneficiaryApi = rtkQueryService.injectEndpoints({
  endpoints: (build) => ({
    getContactUsDetails: build.query<Types.GetContactUsDetailsResponse, number>({
      query: (id) => ({
        url: `${ENDPOINTS.GET_CONTACT_US_DETAILS}/${id}`,
        method: 'GET',
      }),
    }), ...

Hello! I know what is your problem now! 😎 You need to make at least one endpoint under your createApi(), before injecting with injectEndpoints()! In your case, as you only have one endpoint, then you replace it under the createApi(). I know this could be strange, but this is how it works.

So try this:

export const rtkQueryService = createApi({
  tagTypes: ['accountBalance'],
  reducerPath: 'rtkQueryService',
  baseQuery: baseQueryWithInterceptor,
  endpoints: (build) => ({
    getContactUsDetails: build.query<Types.GetContactUsDetailsResponse, number>({
      query: (id) => ({
        url: `${ENDPOINTS.GET_CONTACT_US_DETAILS}/${id}`,
        method: 'GET',
      }),
    })
});

And disable your injectEndpoints().

Read more comments on GitHub >

github_iconTop Results From Across the Web

React/Redux: TypeError: Cannot read property of undefined
TypeError : Cannot read property 'newsItems' of undefined. The error occurs in the mapStateToProps function on line newsItems: state.
Read more >
Getting 'Cannot read properties of undefined (reading 'data ...
I'm fairly new to testing and I'm trying to test a simple React app which uses Redux Toolkit to fetch and render data...
Read more >
Redux: Uncaught TypeError: Cannot read properties of ...
Uncaught TypeError: Cannot read properties of undefined (reading 'mySlice'). This was caused by a circular dependency in my file structure.
Read more >
cannot read properties of null (reading 'usecontext') jest
AAMCODE Asks: Jest TypeError: Cannot read properties of undefined (reading 'content') I am unit testing a React component using Jest and trying to...
Read more >
createApi - Redux Toolkit
createApi is the core of RTK Query's functionality. It allows you to define a set of "endpoints" that describe how to retrieve data...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found