Я следую Руководству по тестированию на написание Redux, чтобы протестировать свое приложение Redux/TypeScript. Я столкнулся с ошибкой TypeError: store.dispatch is not a function
.
Я последовал этому предложению по экспорту и использованию уже настроенного объекта хранилища, но все равно получаю сообщение об ошибке. Что мне не хватает?
(моя попытка)
export default function setupStore(preloadedState?: PreloadedState<RootState>) {
return configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
preloadedState,
});
};
Вот файлы с соответствующим кодом.
test-utils.tsx
import setupStore from '../store';
interface ExtendedRenderOptions extends Omit<RenderOptions, 'queries'> {
preloadedState?: PreloadedState<RootState>;
store?: AppStore;
}
export function renderWithProviders(
ui: React.ReactElement,
{
preloadedState = {},
store = setupStore(preloadedState),
...renderOptions
}: ExtendedRenderOptions = {},
) {
function Wrapper({ children }: PropsWithChildren<{}>): JSX.Element {
return <Provider store = {store}>{children}</Provider>;
}
return { store, ...render(ui, { wrapper: Wrapper, ...renderOptions }) };
}
store.ts
const persistConfig = {
key: 'root',
version: 1,
storage,
};
const rootReducer = combineReducers({ user: userReducer, cart: cartReducer });
const persistedReducer = persistReducer(persistConfig, rootReducer);
export default function setupStore(preloadedState?: PreloadedState<RootState>) {
return configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
preloadedState,
});
};
export let persistor = persistStore(setupStore as any);
export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof setupStore>;
export type AppDispatch = AppStore['dispatch'];
Navbar.tsx
const Navbar: React.FC = () => {
const cartQuantity = useAppSelector((state: RootState) => state.cart.cartQuantity);
const { currentUser } = useAppSelector((state: RootState) => state.user);
return (
<Language>EN</Language>
);
};
Navbar.test.tsx
it('renders same span text in Navbar component', async () => {
renderWithProviders(<Navbar />);
const spanElement = screen.getByText(/EN/i);
expect(spanElement).toBeInTheDocument();
});
Я думаю, что вы столкнулись с той же проблемой, что и сообщение, на которое вы ссылались, с функцией setupStore
, и вам нужно вызвать ее, чтобы получить реальный объект store
.
AppDispatch
должен быть typeof store.dispatch
. Попробуйте следующее, где AppDispatch
«выбирается» из типа AppStore
.
store.ts
export default function setupStore(preloadedState?: PreloadedState<RootState>) {
return configureStore({
reducer: persistedReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: [FLUSH, REHYDRATE, PAUSE, PERSIST, PURGE, REGISTER],
},
}),
preloadedState,
});
};
export type RootState = ReturnType<typeof rootReducer>;
export type AppStore = ReturnType<typeof setupStore>;
export type AppDispatch = Pick<AppStore, "dispatch">;
index.tsx — импортируйте setupStore
и persistStore
сюда и создайте экземпляры store
persistor
объектов.
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
import App from "./App";
import { Provider } from "react-redux";
import { PersistGate } from "redux-persist/integration/react";
import persistStore from "redux-persist/es/persistStore";
import setupStore from "./store";
const store = setupStore();
const persistor = persistStore(store);
ReactDOM.render(
<Provider store = {store}>
<PersistGate loading = {null} persistor = {persistor}>
<App />
</PersistGate>
</Provider>,
document.getElementById("root")
);
@ ln09nv2 Понятно. Я пробовал вышеописанное в проекте, который я нахожусь в процессе перехода на использование RTK, и экспорт этих типов имел ту же подпись, что и мой обычный экспорт export type AppDispatch = typeof store.dispatch;
, поэтому он выглядел эквивалентным. Как вы думаете, вы могли бы создать работающую демонстрацию codeandbox вашего кода, которая воспроизводит проблему, которую мы могли бы проверить вживую?
Мне не удалось импортировать весь мой проект в CodeSandbox, так как я достиг максимального предела в 500 модулей. Вместо этого я вручную скопировал/вставил соответствующие файлы, чтобы воспроизвести проблему. Вот так
@ ln09nv2 Код в вашей песочнице (и, вероятно, реальный код, на котором он основан) не компилировался, потому что вы не передавали фактический store
объект в persistStore
функцию. Переместите создание persistor
в index.ts
, где вы должны создать экземпляр объекта store
, который будет передан компоненту Redux Provider
и persistor
в PersistGate
. Я обновил свой ответ и включил рабочую вилку вашей песочницы.
Это сработало! Я был смущен передачей фактического объекта хранилища при следовании руководству. Спасибо!
Я ценю ваш ответ. Я пытался
export type AppDispatch = typeof setupStore.dispatch
иexport type AppStore = ReturnType<typeof setupStore>;
, но, к сожалению, все еще получаю ту же ошибку.