I am trying to update the state.user.price
state to some other value using reduxjs/toolkit
but it is not working properly. I know this has to do with immutablity then I tried returning new objects without changing them but it didn't work.
const initialUserState = {
isLoggedIn: !!localStorage.getItem("token"),
carName: "",
price: "any",
avaliablity: "any",
type: "any"
}
const userSlice = createSlice({
name: "user",
initialState: initialUserState,
reducers: {
handleAuthState: state => {
state.isLoggedIn = !!localStorage.getItem("token");
},
changeCarName: (state, action) => {
state.carName = action.payload;
},
changeAvailablity: (state, action) => {
state.avaliablity = action.payload;
},
changeType: (state, action) => {
state.type = action.payload;
},
changePrice: (state, action) => {
state.price = action.payload;
}
}
});
const store = configureStore({
reducer: {
UI: UISlice.reducer,
user: userSlice.reducer,
car: carSlice.reducer
}
});
export default store;
export const userActions = userSlice.actions;
const Filters = () => {
const type = useSelector(state => state.user.type);
const price = useSelector(state => state.user.price);
const carName = useSelector(state => state.user.carName);
const availablity = useSelector(state => state.user.availablity);
const sendRequest = async () => {
console.log("priceInSendRequestFunction = ", price);
}
const priceHandler = event => {
if (event.target.value === "") {
dispatch(userActions.changePrice("any"))
} else {
console.log("event.target.value = ", event.target.value);
dispatch(userActions.changePrice(event.target.value));
}
sendRequest();
}
return (
<Select onChange={priceHandler} placeholder="Select Price">
<option value="2000">Below 2000</option>
<option value="4000">2000 - 4000</option>
<option value="6000">4000 - 6000</option>
<option value="infinity">Above 6000</option>
<option value="any">Any</option>
</Select>
)
}
The value of price
in sendRequest
function should get the updated value but it is getting another value.
The current selected price
state is closed over in callback scope when priceHandler
is called. priceHandler
dispatches an action to the store and immediately calls sendRequest
. The component has yet to rerender and access any updated Redux state values.
You can forward the value to sendRequest
that you are updating the state to:
const sendRequest = async (price) => {
console.log("priceInSendRequestFunction = ", price);
};
const priceHandler = event => {
const { value } = event.target;
const newPrice = value || "any";
console.log({ newPrice });
dispatch(userActions.changePrice(newPrice));
sendRequest(newPrice);
};
Or you can import the store
and access the current state directly:
import store from '../path/to/store';
...
const sendRequest = async () => {
const state = store.getState();
const { price } = state.user;
console.log("priceInSendRequestFunction = ", price);
};
const priceHandler = event => {
const { value } = event.target;
const newPrice = value || "any";
console.log({ newPrice });
dispatch(userActions.changePrice(newPrice));
sendRequest();
};
If the sendRequest
function/logic is effectively a "side-effect" then an alternative might be to convert the changePrice
action into an asynchronous action, e.g. a Thunk.
import { createAsyncThunk, createSlice } from '@reduxjs/toolkit';
const initialUserState = {
isLoggedIn: !!localStorage.getItem("token"),
carName: "",
price: "any",
avaliablity: "any",
type: "any"
};
export const changePrice = createAsyncThunk(
"user/changePrice",
async (price, thunkAPI) => {
console.log("price in changePrice action = ", price);
try {
// side-effect to send price ...somewhere
...
return price;
} catch(error) {
return thunkAPI.rejectWithValue(error);
}
}
);
const userSlice = createSlice({
name: "user",
initialState: initialUserState,
reducers: {
handleAuthState: state => {
state.isLoggedIn = !!localStorage.getItem("token");
},
changeCarName: (state, action) => {
state.carName = action.payload;
},
changeAvailablity: (state, action) => {
state.avaliablity = action.payload;
},
changeType: (state, action) => {
state.type = action.payload;
},
},
extraReducers: builder => {
builder
// Update local price state if side-effect successful
.addCase(changePrice.fulfilled, (state, action) => {
state.price = action.payload;
});
},
});
const Filters = () => {
const dispatch = useDispatch();
const {
availablity,
carName,
price,
type,
} = useSelector(state => state.user);
const priceHandler = event => {
const { value } = event.target;
const newPrice = value || "any";
console.log({ newPrice });
dispatch(userActions.changePrice(newPrice));
}
return (
<Select onChange={priceHandler} placeholder="Select Price">
<option value="2000">Below 2000</option>
<option value="4000">2000 - 4000</option>
<option value="6000">4000 - 6000</option>
<option value="infinity">Above 6000</option>
<option value="any">Any</option>
</Select>
)
}