Create Dynamic Url at Redux Toolkit - javascript

I have an initialState and a dynamic URL and async thunk for axios
I want to change URL query parameter according to a key in initial state.
?query=${initialState.category}
category changes but URL fetches data with unchanged category (it uses initial key not modified key)
const initialState = {
category: "burger",
products: [],
isLoading: true,
categories :{
burger:"burger",
kebab:"kebab",
chicken:"chicken",
pizza:"pizza",
fish:"fish",
vegan:"vegan",
salad:"salad",
pasta:"pasta",
steak:"steak",
dessert:"dessert",
waffle:"waffle"
}
};
const url = `https://api.spoonacular.com/food/menuItems/search?query=${initialState.category}&number=10&apiKey=API_KEY`;
export const getProducts = createAsyncThunk(
"products/getProducts",
async (_, thunkAPI) => {
try {
const response = await axios(url);
console.log(response);
return await response.data;
} catch (error) {
return thunkAPI.rejectWithValue({ error: error.message });
}
}
);
export const productSlice = createSlice({
name: "product",
initialState,
reducers: {
pickCategory: (state, action) => {
state.category = action.payload;
},

that line is static as value in initialState is not mutated:
const url = `https://api.spoonacular.com/food/menuItems/search?query=${initialState.category}&number=10&apiKey=API_KEY`;
you need to move that constant inside getProducts thunk as thunk has an access to current store:
...
async (_, thunkAPI) => {
const {category} = thunkApi.getState()
const url = `https://api.spoonacular.com/food/menuItems/search?
query=${category}&number=10&apiKey=API_KEY`;
}
...

Related

How to retrieve object after post in react redux from api

I created a Todo application in React.js. I have an API to store my todos and their states.
In my application I can drag and drop the todos in the state I want and I can also CRUD the states.
For the state part I use React-Redux.
My problem happens when adding a new state, I would like to get the id that is provided by the API but in my reducer I get this error: A non-serializable value was detected in the state, in the path: stateReducer.
If I don't get the id, I can't delete the state cause I delete by id, I have to reload the page.
To initialize my state list I use a middleware to do the asymmetric part but I don't understand how to do it when I add a new object.
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";
import Api from "../api/Api.ts";
const api = new Api("http://localhost:3333/api/state/")
const initialState = {
status:'idle',
list:[],
error: null
}
export const StateSlice = createSlice({
name:'status',
initialState,
reducers: {
add: async (state, action) => {
let item
await api.post(action.payload).then(state => item = state) // PROBLEME
state.list.push(item)
},
remove:(state, action) => {
state.list.splice(state.list.indexOf(action.payload),1)
api.del(action.payload)
},
update:(state, action) => {
api.update(action.payload)
},
get: async (state,action) => {
state.list = action.payload
},
},
extraReducers(builder) {
builder
.addCase(fetchPosts.pending, (state, action) => {
state.status = 'loading'
})
.addCase(fetchPosts.fulfilled, (state, action) => {
state.status = 'succeeded'
state.list = state.list.concat(action.payload)
})
.addCase(fetchPosts.rejected, (state, action) => {
state.status = 'failed'
state.error = action.error.message
})
}
})
const getPosts = async () => {
const states = []
let posts = await api.get()
posts.forEach(p => {
states.push({id:p.id,name:p.name})
});
return states
}
export const fetchPosts = createAsyncThunk('state/fetchPosts', async () => {
let response = await getPosts()
return response
})
export const {add, remove, update, get} = StateSlice.actions
export default StateSlice.reducer;
Do I need to create another middleware for this and how call it ? I am new to this technology, can you give me a hint please?

React Redux Toolkit - Can we Dispatch/Call from one reducer's action to another reducer's action to change state variable

Here I have two state slices and I need to dispatch a method of slice1 within slice2.
How can I call a reducer's action of slice 1 from extra reducer's action of callApiSlice
const slice1 = createSlice({
initialState,
reducers: {
login: (state) => {
state.login = { email: 'email#gmail.com', api_keys: false};
},
setApiKey: (state) => {
state.login.api_keys = true;
},
},
}
export const callApi = createAsyncThunk(
"call-api",
async (payload, thunkAPI) => {
try {
const res = await axios.post( process.env.REACT_APP_API_URL + "/save", payload);
return res.data;
} catch (error) {
return thunkAPI.rejectWithValue(error.response.data);
}
}
);
const callApiSlice = createSlice({
name: "callApiSlice",
initialState,
reducers: {},
extraReducers: {
[callApi.fulfilled]: (state, action) => {
// how to call Slice 1 reducer's action setApiKey to change in login state
}
}
});
export default callApiSlice.reducer;
You can't directly invoke reducer functions, but if I'm correctly understanding your question it seems you want "setApiKey" reducer function to run upon dispatch of the callApi.fulfilled action. Redux state slices/reducer functions (i.e. the state reducer tree) are technically passed every action that is dispatched, and can respond to any of them. Add a reducer case in the slice1 state slice to handle the callApi.fulfilled action.
Example:
const slice1 = createSlice({
name: "slice1",
initialState,
reducers: {
login: (state) => {
state.login = { email: 'email#gmail.com', api_keys: false };
}
setApiKey: (state) => {
state.login.api_keys = true;
}
},
extraReducers: {
[callApi.fulfilled]: (state) => { // <-- respond/react to action
state.login.api_keys = true;
},
},
}
export const callApi = createAsyncThunk(
"call-api",
async (payload, thunkAPI) => {
try {
const { data } = await axios.post( process.env.REACT_APP_API_URL + "/save", payload);
return data;
} catch (error) {
return thunkAPI.rejectWithValue(error.response.data);
}
}
);
const callApiSlice = createSlice({
name: "callApiSlice",
initialState,
extraReducers: {
[callApi.fulfilled]: (state, action) => {
...
}
},
});

Redux toolkit axios api is not getting called

Can someone please help me to find what exact problem is with below code, as i am new in redux.
API is not called
data is not coming to state products[] array
product-slice.js
import { createAsyncThunk, createSlice } from "#reduxjs/toolkit";<br/>
import axios from "axios";<br/>
const initialState = {<br/>
products: [],<br/>
sample: "Hello World",<br/>
};<br/>
<br/>
const productSlice = createSlice({<br/>
name: "productSlice",<br/>
initialState,<br/>
reducers: {<br/>
getProducts(state, action) {<br/>
const p = action.payload;<br/>
console.log(p);<br/>
},<br/>
},<br/>
extraReducers: (builder) => {<br/>
builder.addCase(fetchProductData.fulfilled, (state, action) => {<br/>
state.products.push(action.payload);<br/>
});<br/>
},<br/>
});<br/>
export const fetchProductData = createAsyncThunk(<br/>
"products/fetchProducts",<br/>
async (_, thunkAPI) => {<br/>
try {<br/>
const response = await axios.get("http://localhost:8080/products");<br/>
return await response.data;<br/>
} catch (error) {<br/>
return error;<br/>
}<br/>
}<br/>
);<br/>
export const productActions = productSlice.actions;<br/>
export default productSlice;<br/>
ProductList.js(Component)<br/>
const ProductList = (props) => {<br/>
const history = useHistory();<br/>
const hello = useSelector((state) => state.products.sample);<br/>
const dispatch = useDispatch();<br/>
<br/>
useEffect(() => {<br/>
dispatch(fetchProductData);<br/>
}, [dispatch]);<br/>
You should call an action as a function inside dispatch() like dispatch(fetchProductData()) and implement redux/toolkit with thunk middleware like below (example):
as you can see in reduxToolkitCreateAsyncThunk
import { createAsyncThunk, createSlice } from '#reduxjs/toolkit'
import { userAPI } from './userAPI'
// First, create the thunk
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
// Then, handle actions in your reducers:
const usersSlice = createSlice({
name: 'users',
initialState: { entities: [], loading: 'idle' },
reducers: {
// standard reducer logic, with auto-generated action types per reducer
},
extraReducers: (builder) => {
// Add reducers for additional action types here, and handle loading state as needed
builder.addCase(fetchUserById.fulfilled, (state, action) => {
// Add user to the state array
state.entities.push(action.payload)
})
},
})
// Later, dispatch the thunk as needed in the app
dispatch(fetchUserById(123))

POST request response undefined , but REQUEST works

userSlice
import { createSlice, createAsyncThunk } from "#reduxjs/toolkit";
import LoginService from "../../Services/Login.service";
export const userRegister = createAsyncThunk(
"users/register",
async (params) => {
try {
const { registerForm } = params;
const { data } = await LoginService.register(registerForm);
return data;
} catch (error) {
}
}
);
const initialState = {
userData: {},
errorResponse: null,
status: "idle",
};
export const userSlice = createSlice({
name: "User",
initialState,
reducers: {},
extraReducers: {
[userRegister.pending]: (state, action) => {
state.status = "loading";
},
[userRegister.fulfilled]: (state, action) => {
state.status = "succeeded";
state.userData = action.payload;
},
[userRegister.error]: (state, action) => {
state.status = "failed";
state.errorResponse = action.payload;
},
},
});
export default userSlice.reducer;
Login.service.js
import axios from "axios";
const API = axios.create({ baseURL: 'http://localhost:3001'});
const LoginService = {
register: async (registerData ) => {
await API.post('/users/register', registerData)
}
};
export default LoginService;
Hi.I try add register feature to my app. But when i submit register form, the datas is saved to the database without any problems. But this line const data = await LoginService.register(registerForm); doesnt work data is undefined but when i same post request in postman i get response data the way i want.
LoginService.register is not returning anything,
you can fix that by doing:
const LoginService = {
register: async (registerData ) => {
const response = await API.post('/users/register', registerData);
return response.data;
}
};

Redux Toolkit and Axios

I'm using Redux Toolkit to connect to an API with Axios.
I'm using the following code:
const products = createSlice({
name: "products",
initialState: {
products[]
},
reducers: {
reducer2: state => {
axios
.get('myurl')
.then(response => {
//console.log(response.data.products);
state.products.concat(response.data.products);
})
}
}
});
axios is connecting to the API because the commented line to print to the console is showing me the data. However, the state.products.concat(response.data.products); is throwing the following error:
TypeError: Cannot perform 'get' on a proxy that has been revoked
Is there any way to fix this problem?
I would prefer to use createAsyncThunk for API Data with extraReducers
Let assume this page name is productSlice.js
import { createSlice,createSelector,PayloadAction,createAsyncThunk,} from "#reduxjs/toolkit";
export const fetchProducts = createAsyncThunk(
"products/fetchProducts", async (_, thunkAPI) => {
try {
//const response = await fetch(`url`); //where you want to fetch data
//Your Axios code part.
const response = await axios.get(`url`);//where you want to fetch data
return await response.json();
} catch (error) {
return thunkAPI.rejectWithValue({ error: error.message });
}
});
const productsSlice = createSlice({
name: "products",
initialState: {
products: [],
loading: "idle",
error: "",
},
reducers: {},
extraReducers: (builder) => {
builder.addCase(fetchProducts.pending, (state) => {
state. products = [];
state.loading = "loading";
});
builder.addCase(
fetchProducts.fulfilled, (state, { payload }) => {
state. products = payload;
state.loading = "loaded";
});
builder.addCase(
fetchProducts.rejected,(state, action) => {
state.loading = "error";
state.error = action.error.message;
});
}
});
export const selectProducts = createSelector(
(state) => ({
products: state.products,
loading: state.products.loading,
}), (state) => state
);
export default productsSlice;
In your store.js add productsSlice: productsSlice.reducer in out store reducer.
Then for using in component add those code ... I'm also prefer to use hook
import { useSelector, useDispatch } from "react-redux";
import {
fetchProducts,
selectProducts,
} from "path/productSlice.js";
Then Last part calling those method inside your competent like this
const dispatch = useDispatch();
const { products } = useSelector(selectProducts);
React.useEffect(() => {
dispatch(fetchProducts());
}, [dispatch]);
And Finally, you can access data as products in your component.
It is happening because your reducer function is not a pure function, it should not be having any asynchronous calls.
something like this should work. it will fix the error you are getting
const products = createSlice({
name: "products",
initialState: {
products: []
},
reducers: {
reducer2: (state, { payload }) => {
return { products: [...state.products, ...payload]}
})
}
}
});
and api should be called outside
const fetchProducts = () => {
axios.get('myurl')
.then(response => {
//console.log(response.data.products);
store.dispatch(products.actions.reducer2(response.data.products))
})
};
PS: haven't tried running above code, you may have to make modifications as per your need.

Categories