I am using redux toolkit and firebase firestore for backend. I just want to get an array of objects from the database. Below is the code for slice. When I log the payload in the console I am unable to get the data. Thanks in advance.
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import firestore from '#react-native-firebase/firestore';
export const getCarouselImages = createAsyncThunk("/carouselImages", () =>
firestore().collection('Users').get()
);
const initialState = {
isLoading: false,
failed: true,
success: false,
imageArray:[],
};
const carouselData = createSlice({
name: "carouselImageSlice",
initialState,
reducers: {
resetCarouselData: () => initialState,
},
extraReducers: (builder) => {
builder.addCase(getCarouselImages.fulfilled, (state, { payload }) => {
state.isLoading = false;
state.failed = false;
state.success = true;
payload.forEach(doc => {
state.userData.push(doc.data())
});
console.log(payload)
});
builder.addCase(getCarouselImages.rejected, (state, action) => {
state.isLoading = false;
state.failed = true;
state.success = false;
});
builder.addCase(getCarouselImages.pending, (state, { payload }) => {
state.isLoading = true;
state.failed = false;
state.success = false;
});
},
});
export const { resetCarouselData } = carouselData.actions;
export default carouselData.reducer;
Related
IMAGE LINK HERE. it doubles the initial return.
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit'
import axios from 'axios'
let url = 'http://localhost:3000/api/v1/posts'
const initialState = {
allPosts: [],
isLoading: false,
isError: false,
}
export const getAllPost = createAsyncThunk('allPosts/getAllPost', async (page, thunkAPI) => {
console.log(page)
try {
const { data } = await axios.get(`${url}?page=${page}`)
return data.posts
} catch (error) {
console.log(error)
}
})
const allPostSlice = createSlice({
name: 'allPosts',
initialState,
reducers: {},
extraReducers: {
[getAllPost.pending]: (state) => {
state.isLoading = true
},
[getAllPost.fulfilled]: (state, { payload }) => {
console.log(payload)
state.allPosts = [...state.allPosts, ...payload]
state.isLoading = false
},
[getAllPost.rejected]: (state) => {
state.isLoading = false
state.isError = true
},
},
})
export default allPostSlice.reducer
When I try to append ...state.allPosts, which is supposed to be the old state, it prints double the initial payload. I only need it printed once, not twice.
[getAllPost.fulfilled]: (state, { payload }) => {
console.log(payload)
state.allPosts = [...state.allPosts, ...payload]
state.isLoading = false
},
How can I print my payload only once using state.allPosts?
I am trying to auth session by random user with http get request and createAsyncThunk.
fetching the user data in App.js on mount hook.
I can see the get request in my network and the new fetched state in redux dev tool,
but my TopBar.js useSelector return the initial state before the fetch.
TopBar.js user log:
App.js:
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchRandomUserData())
}, [dispatch]);
authSlice.js:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
const initialState = {
isLoggedIn: true,
user: {},
loading: false,
error: null,
};
export const fetchRandomUserData = createAsyncThunk(
'auth/fetchRandomUser',
async () => {
try {
const response = await fetch('https://randomuser.me/api/');
const data = await response.json();
return data.results[0];
} catch (error) {
throw Error(error);
}
}
);
const authSlice = createSlice({
name: 'auth',
initialState,
reducers: {
logout(state, action) {
state.isLoggedIn = false;
},
},
extraReducers: {
[fetchRandomUserData.pending]: (state, action) => {
state.loading = true;
state.error = null;
},
[fetchRandomUserData.fulfilled]: (state, action) => {
console.log("action.payload",action.payload);
state.user = action.payload;
state.loading = false;
},
[fetchRandomUserData.rejected]: (state, action) => {
state.error = action.error.message;
state.loading = false;
},
},
});
export const { logout } = authSlice.actions;
export default authSlice.reducer;
store.js
import { configureStore } from '#reduxjs/toolkit';
// import booksReducer from './reducers/booksReducer';
import booksReducer from './slices/bookSlice';
import authReducer from './slices/authSlice';
const store = configureStore({
reducer: { books: booksReducer, auth: authReducer },
});
export default store;
TopBat.js:
export default function TopBar(): JSX.Element {
const user = useSelector((state: any) => state.auth);
console.log("topbar",user); // returns the initial state
//....
Please make sure that you update react-redux to version 8 if you are using react 18.
There are known cases where components just stop updating if you are using react-redux 7 and lower with react 18.
I'm trying to achieve automated refetching without using RTK Query.
I.e. im writing my API calls using createAsyncThunk & Axios.
On create(post) or update(patch) request being successful, I would rather just "refetch" data, rather than push my changes into the original data set.
This is how it would be done with RTK Query:https://redux-toolkit.js.org/rtk-query/usage/automated-refetching
By invalidating the data.
A simple solution would be if I could call my "fetchClients" thunk..
Any ideas?
import { createSlice, createAsyncThunk, current } from "#reduxjs/toolkit/";
import axios from "axios";
import { getAxiosHeaders } from "../Api/apiHelper";
const initialState = {
clients: [],
client: null,
status: "idle", //'idle' | 'loading' | 'succeeded' | 'failed'
error: null,
isModalOpen: false,
};
export const fetchClients = createAsyncThunk("clients/fetchClients", async () => {
alert("hello fetch clients");
const response = await axios.get(CLIENTS_URL, { headers });
return response.data.clients;
});
export const addNewClient = createAsyncThunk("clients/addNewClient", async (body) => {
const response = await axios.post(CLIENTS_URL, body, { headers });
return response.data;
});
export const clientsSlice = createSlice({
name: "clients",
initialState,
reducers: {
toggleModal: (state, action) => {
state.isModalOpen = !action.payload;
},
},
},
extraReducers: (builder) => {
builder
.addCase(fetchClients.pending, (state, action) => {
state.status = "loading";
})
.addCase(fetchClients.fulfilled, (state, action) => {
state.status = "success";
state.clients = action.payload;
})
.addCase(fetchClients.rejected, (state, action) => {
state.status = "failed";
state.error = action.error.message;
})
.addCase(addNewClient.fulfilled, (state, action) => {
console.log(state.clients);
// REFETCH DATA i.e. fetchClients()
// state.clients.push(action.payload);
})
},
});
export const { clientAdded, toggleModal, setClient } = clientsSlice.actions;
export default clientsSlice.reducer;
I have separated slices for every service from backend to control different states like loading and error etc., but don't know if there is better way to structure slices.
File structure for now looks like this:
featureName/service1Slice;
featureName/service2Slice;
featureName/service3Slice;
feature2Name/service1Slice;
etc...
And slice itself:
import { createSlice, createAsyncThunk } from '#reduxjs/toolkit';
import { IContract} from 'contracts';
import { ServiceName } from 'services';
interface IAllStatuses {
loading: boolean;
whoseDataIsLoading?: string;
error: boolean;
errorText?: string | unknown;
}
const initialState: IAllStatuses = {
loading: false,
whoseDataIsLoading: '',
error: false,
errorText: '',
};
export const serviceName = createAsyncThunk('servicename', async (request: IContract) => {
const response = await ServiceName(request);
return response;
});
export const serviceNameSlice = createSlice({
name: 'service-name',
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(serviceName.fulfilled, (state) => {
state.loading = false;
});
builder.addCase(serviceName.pending, (state, action) => {
state.loading = true;
state.whoseDataIsLoading = action.payload;
});
builder.addCase(serviceName.rejected, (state, action) => {
state.error = true;
state.loading = false;
state.errorText = action.payload;
});
},
});
export const serviceNameReducer= serviceNameSlice.reducer;
For the first time, I am using Redux in my React project. The code here I have added is for cookie-based authentication. I am worried that everything is here is in the correct format. It seems lots of duplicate code here. Especially for pending and rejected status in createSlice portion. How can I refactor this code and what will be the correct coding style in this case?
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import API from "../API";
// Register user:
export const signup = createAsyncThunk(
"user/signup",
async (userInfo, { rejectWithValue }) => {
try {
const { data } = await API.post("/signup", userInfo);
return data;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
);
// Login:
export const login = createAsyncThunk(
"user/login",
async (loginInfo, { rejectWithValue }) => {
try {
const { data } = await API.post("/login", loginInfo);
return data;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
);
// Logout:
export const logout = createAsyncThunk(
"user/logout",
async (args, { rejectWithValue }) => {
try {
const { data } = await API.get("/logout");
return data;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
);
// Chek-Auth:
export const isAuthenticated = createAsyncThunk(
"user/isAuthenticated",
async (args, { rejectWithValue }) => {
try {
const { data } = await API.get("/check-auth");
return data;
} catch (error) {
return rejectWithValue(error.response.data);
}
}
);
// createSlice portion is here:
export const userSlice = createSlice({
name: "user",
initialState: {
loading: true,
isLoggedIn: false,
message: "",
user: null,
error: null,
},
reducers: {},
extraReducers: {
[signup.pending]: (state, action) => {
state.loading = true;
},
[signup.fulfilled]: (state, action) => {
state.loading = false;
state.isLoggedIn = true;
state.message = action.payload.message;
state.user = action.payload.user;
state.error = null;
},
[signup.rejected]: (state, action) => {
state.loading = false;
state.error = action.payload || action.error;
},
[login.pending]: (state, action) => {
state.loading = true;
},
[login.fulfilled]: (state, action) => {
state.loading = false;
state.isLoggedIn = true;
state.message = action.payload.message;
state.user = action.payload.user;
state.error = null;
},
[login.rejected]: (state, action) => {
state.loading = false;
state.error = action.payload || action.error;
},
[logout.pending]: (state, action) => {
state.loading = true;
},
[logout.fulfilled]: (state, action) => {
state.loading = false;
state.isLoggedIn = false;
state.message = action.payload.message;
state.user = null;
},
[logout.rejected]: (state, action) => {
state.loading = false;
state.error = action.payload || action.error;
},
[isAuthenticated.pending]: (state, action) => {
state.loading = true;
},
[isAuthenticated.fulfilled]: (state, action) => {
state.loading = false;
state.isLoggedIn = true;
state.message = action.payload.message;
state.user = action.payload.user;
},
[isAuthenticated.rejected]: (state, action) => {
state.loading = false;
state.error = action.payload || action.error;
},
},
});
// export const { } = userSlice.actions;
export default userSlice.reducer;
We generally recommend to use the builder notation, not the object notation. That makes stuff like this easier:
extraReducers: builder => {
for (const thunk in [signup, login, logout, isAuthenticated]) {
builder.addCase(thunk.pending, (state) => { state.loading = true })
builder.addCase(thunk.rejected, (state, action) => {
state.loading = false;
state.error = action.payload || action.error;
})
}
}
Keep in mind though that putting many asynchronous actions in the same state like you do here, sharing a loading state, may lead to race conditions.
Generally, for api cache stuff you should take a look into Redux Toolkit's Api cache abstraction, RTK-Query:
https://redux-toolkit.js.org/rtk-query/overview