I tried to follow the documentation and this is what I did
middleware/index.ts
import { combineEpics } from "redux-observable";
import userEpic from "./userEpic";
export const rootEpic = combineEpics(
userEpic,
);
store.ts
import { configureStore } from "@reduxjs/toolkit";
import { createEpicMiddleware } from "redux-observable";
import { rootEpic } from "../middleware";
import userReducer from "./reducers/userSlice";
const rootReducer = {
user: userReducer,
};
const epicMiddleware = createEpicMiddleware();
export const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(epicMiddleware),
});
epicMiddleware.run(rootEpic);
export type RootStateType = ReturnType<typeof store.getState>;
export type AppDispatchType = typeof store.dispatch;
export default store;
actions.ts
export const LOGIN_USER = "LOGIN_USER";
export const loginUser = () => {
return {
type: LOGIN_USER,
} as const;
};
export type LoginUserAction = ReturnType<typeof loginUser>;
userEpic.ts (this is where the API call should be but I made this way just to test if it is working)
import { Action } from "@reduxjs/toolkit";
import { Observable } from "rxjs";
import { combineEpics, ofType } from "redux-observable";
import { LOGIN_USER } from "./actions";
import { map } from "rxjs/operators";
import { setUserData } from "../../redux/reducers/userSlice";
const fetchUserEpic = (action$: Observable<Action>) => {
return action$.pipe(
ofType(LOGIN_USER),
map(() =>
setUserData({
id: 12,
name: "test",
surname: "test",
username: "test",
refreshToken: "test",
accessToken: "test",
})
)
);
};
export default combineEpics(fetchUserEpic);
userSlice.ts
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { FetchStatus } from "../../modules/types";
import { RootStateType } from "../store";
type UserType = {
id: number;
name: string;
surname: string;
username: string;
refreshToken: string;
accessToken: string;
};
type UserStateType = {
user: UserType | null;
userFetchStatus: FetchStatus;
};
const initialUserState: UserStateType = {
user: null,
userFetchStatus: FetchStatus.success,
};
export const userSlice = createSlice({
name: "user",
initialState: initialUserState,
reducers: {
setUserData: (state, action: PayloadAction<UserType>) => {
state.user = action.payload;
state.userFetchStatus = FetchStatus.success;
},
setUserFetchingStatus: (state, action: PayloadAction<FetchStatus>) => {
state.userFetchStatus = action.payload;
},
clearUserData: () => {
return initialUserState;
},
},
extraReducers: (builder) => {
builder.addDefaultCase((state) => {
return state;
});
},
});
export const { setUserData, clearUserData } = userSlice.actions;
export const getUserData = (state: RootStateType) => state.user;
export default userSlice.reducer;
And I have some component where I trigger loginUser
and then try to console log it from the redux
component.ts
const dispatch = useAppDispatch()
const user = useAppSelector(getUserData);
console.log(user)
<TouchableOpacity onPress={() => dispatch(loginUser)}>
//...
package.json
"react-redux": "^8.0.1",
"redux": "^4.2.0",
"redux-observable": "^2.0.0"
Looks like the redux store is not changing, loginUser
action is triggering but looks like ofType(LOGIN_USER)
is not catching it, I saw in some other thread and documentation to use filter(actionFunction.match),
instead of ofType
, but I think that is only for redux actions? And when I use the filter I get an error that is deprecated.
You have to call your action creator:
dispatch(loginUser())
, not dispatch(loginUser)
.
Apart from that, you should definitely not be writing that action creator by hand - if you don't want to make it with createSlice
, at least use createAction
for it.
export const loginUser = createAction('user/login')
.
Then you can also use filter(loginUser.match)
with it.