react redux thunk not populating state object - reactjs

im having an issue with my code, its not populating the state object when state action is being performed. im new with redux
i have this code. so far that having an issue
this is the statement that will called the props.action fetchProjectFamilyList
case 'SubBusinessUnit':
setProductFamilyDetailsObj([])
if (selectedOption.id != 0) {
props.actions.fetchDepartment(selectedOption.id)
props.actions.fetchProjectFamilyList(selectedOption.id)
console.log(props)
}
setDropdownDataInState(resetData, 'Department')
setFormFields({
...formFields,
'OtherNamedInsuredIndustry': {
...formFields.OtherNamedInsuredIndustry,
value: ''
},
'NamedInsuredIndustry': {
...formFields.NamedInsuredIndustry,
value: "",
selectedId: 0
},
[fieldName]: {
...formFields[fieldName],
value: selectedOption.description, selectedId: selectedOption.id
}
});
break;
and this is the code for the commonreducer
export const fetchProjectFamilyList = createAsyncThunk(types.FETCH_PROJECT_FAMILY_LIST,
async (option, {getState, rejectWithValue}) => {
const reduxThunkConfig = {
checkStateData:getState().commonReducer.projectFamilyList && getState().commonReducer.projectFamilyList[option],
rejectWithValue
}
const APIConfig = {
URL: "eapi-referencedata/v1/lists/12?filterBySourceList=" + option + "&filterBySourceListValue=15",
method:"getData",
}
console.log('fetchProjectFamilyList')
return fetchCachedData(reduxThunkConfig, APIConfig);
}
)
im using the builder in my case of course inistailstate is set
const initialState = {
projectFamilyList:{},
}
builder.addCase(fetchProjectFamilyList.fulfilled, (state, action) => {
const subDivision = action.meta.arg;
return {
...state,
projectFamilyList:{
...state.projectFamilyList,
[subDivision]: action.payload},
}})
const commonActions = { ...actions, fetchProjectFamilyList }
export { commonActions, commonReducer}
this is the comment that accept the state as props. but the props productFamilyDetailsObj is empty object
<ProductFamilyComponent
productFamilyDetailsObj={productFamilyDetailsObj}
/>
function ProductFamilyComponent({ productFamilyDetailsObj }) {
return <div className="boxLayout">
<p className="smallHeading">Product Families</p>
{productFamilyDetailsObj.map((text, textIndex) => {
let index = textIndex;
return ( .... and so on
I hope theres someone who could help me resolving this. thank in advance.

Related

React - How to get current state values in custom hooks with redux-toolkit?

I've got a question. I have a custom hook like this:
import { isNullOrUndefined } from '../../../../../../utils/utils';
import { useSelector } from 'react-redux';
export const useGoodsSetupWizardController = () => {
const productSetupWizard = useSelector((state) => state.productSetupWizard.value);
function isNextButtonDisabled() {
let returnValue = false;
switch(productSetupWizard?.step) {
case 2:
if(isNullOrUndefined(productSetupWizard?.product?.productName) || productSetupWizard?.product?.productName.trim().length === 0) {
returnValue = true;
}
}
return returnValue;
}
return {
isNextButtonDisabled,
};
}
As you can see I'm using useSelector to get the current value of productSetupWizard.
In my other components and hooks all is working fine using this way.
The slice is this one here:
import { createSlice } from '#reduxjs/toolkit';
const initialState = {
value: {
step: 1,
product: null
},
};
export const productSetupWizardSlice = createSlice({
name: 'productSetupWizard',
initialState,
reducers: {
setProductSetupWizard: (state, action) => {
state.value = action.payload
},
increaseStep: (state, action) => {
state.value.step++;
},
decreaseStep: (state, action) => {
state.value.step--;
}
},
});
export const { setProductSetupWizard, increaseStep, decreaseStep } = productSetupWizardSlice.actions;
export default productSetupWizardSlice.reducer;
I don't know if you need to see the store file here to answer my question. If so, please let me know. I will add them to my question then.
The question I have, it seems not be working in a custom hook like the code above.
It gives me always the initial value of productSetupWizard and not updates on it.
Is it because the custom hook is initialized once on the component body with the initial value of productSetupWizard?
So my question is, is there any special usage in custom hooks necessary to get the state values from React-redux (toolkit)?
So the value of productSetupWizard in method isNextButtonDisabled() is always the initial value:
value: {
step: 1,
product: null
},
Here is the complete way of importing the different custom hooks I use in my project.
I have a hook called ProductSetupWizard. This is my UI. This ProductSetupWizard is importing the function getController from the custom hook called useProductTypeSetupWizard:
import { useProductTypeSetupWizard } from '../../../../factories/productSetup.factory';
function ProductSetupWizard(props) {
const [productSetupController, setProductSetupController] = useState(null);
...
const { getController } = useProductTypeSetupWizard();
...
useEffect(() => {
setProductSetupController(getController(productTypeId));
},[productTypeId]);
return (
<>
...
</>
);
}
The useProductTypeSetupWizard hook is loading and returning the useGoodsSetupWizardController depending on the given productTypeId:
import { isNullOrUndefined } from "../utils/utils";
import { useGoodsSetupWizardController } from '../components/managementInterface/shop/tabs/productSetupWizard/setups/goodsSetupWizard.controller';
export const useProductTypeSetupWizard = () => {
const {
getComponentGoodsController,
isNextButtonDisabledGoodsController,
needsNextStepToBeSkippedGoodsController
} = useGoodsSetupWizardController();
function getController(productTypeId) {
if(isNaN(parseInt(productTypeId))) {
return null;
}
switch(parseInt(productTypeId)) {
case 1:
return {
getComponent: getComponentGoodsController,
isNextButtonDisabled: isNextButtonDisabledGoodsController,
needsNextStepToBeSkipped: needsNextStepToBeSkippedGoodsController
};
case 3:
//apply like useGoodsSetupWizardController
}
}
return {
getController
};
}

How to access the updated value in my store immediately after updating it?

I have a React application that is currently using Redux for state management.
What I am trying to achieve: Click a Buy Now button - dispatch a action that makes a request to the server to add the item (increment the cart item count based on server response), check the state to see if the cart item count is greater than 0 & do something if it is.
For some reason, I have to click the button twice in order for the cartItemCount to reflect 1?
My current implementation looks like the below (I have tried to pull out all the unrelated code due to the file being quite large):
CourseSpecificScreen.tsx
const mapStateToProps = (state: RootState) => {
return {
courseSpecificReducer: state.courseSpecificReducer,
authState: state.authReducer,
currencyState: state.currencyReducer,
cartReducer: state.cartReducer,
courseCategoriesState: state.courseCategoriesReducer,
};
};
const mapDispatchTopProps = (dispatch: Dispatch<AnyAction>) => {
return bindActionCreators(ActionCreators, dispatch);
};
const connector = connect(mapStateToProps, mapDispatchTopProps);
type CourseSpecificScreenNavigationProp = CompositeNavigationProp<
StackNavigationProp<ExploreRouteStackParamList, "CourseSpecificScreen">,
CompositeNavigationProp<
StackNavigationProp<AppRouteHeaderParamList>,
StackNavigationProp<AuthRouteStackParamList>
>
>;
type CourseSpecificScreenRouteProp = RouteProp<
ExploreRouteStackParamList,
"CourseSpecificScreen"
>;
type Props = PropsFromRedux & {
navigation: CourseSpecificScreenNavigationProp;
route: CourseSpecificScreenRouteProp;
};
type State = {
cartItemCount: number;
};
class CourseSpecificScreen extends Component<Props, State> {
pruchaseItem = async () => {
const {
courseSpecificReducer,
clearCartAndAddItem,
navigation,
cartReducer,
getCartItemCount,
} = this.props;
const paymentMethod = paymentMethodForDevice();
await clearCartAndAddItem(
paymentMethod,
courseSpecificReducer.productData.code as string,
1,
navigation
)
if(cartReducer.cartItemCount > 0) {
// do some stuff
}
};
render() {
return (
<Button
btnStyle={[this.getStyles().smallButtonBuyCourse]}
labelStyle={[this.getStyles().buttonStickyLabelStyle]}
label={translate(
productData.isBundle && productData.isBundle === true
? "CategorySpecificScreen_buyThisBundle"
: "CategorySpecificScreen_buyThisCourse",
)}
onPress={this.purchaseItem}
disabled={false}
/>
)
};
CourseSpecificScreen.contextType = LocalizationContext;
export default connector(CourseSpecificScreen);
ThunkActions.ts
export const clearCartAndAddItem = (
paymentMethod: string,
productCode: string,
quantity: number,
navigation: any,
): AppThunk => {
return async (dispatch) => {
dispatch(cartActions.updateCartLoadingStatus(true));
const response = await cartServices.clearCart();
const {httpStatusCode} = response as APIResponse;
switch (httpStatusCode) {
case httpStatusCodes.SUCCESS_OK:
case httpStatusCodes.SUCCESS_CREATED:
case httpStatusCodes.SUCCESS_NO_CONTENT:
dispatch(cartActions.updateCartLoadingStatus(false));
dispatch(cartActions.updateCartItemCount(0))
globalConfig.setCartItemCount(0);
dispatch(addItemToCart(paymentMethod, productCode, quantity, navigation));
break;
case httpStatusCodes.CLIENT_ERROR_UNAUTHORIZED:
case httpStatusCodes.SERVER_ERROR_INTERNAL_SERVER_ERROR:
dispatch(cartActions.updateCartLoadingStatus(false));
let alertMessage = "Error, please try again later.";
if (response?.message) alertMessage = response?.message;
Alert.alert("Alert", alertMessage, [
{
text: "Ok",
},
]);
break;
default: {
dispatch(cartActions.updateCartLoadingStatus(false));
}
}
};
};
export const addItemToCart = (
paymentMethod: string,
productCode: string,
quantity: number,
navigation: any,
): AppThunk => {
return async (dispatch) => {
dispatch(cartActions.updateCartLoadingStatus(true));
const response = await cartServices.addItemToCart(productCode, quantity, paymentMethod);
const {httpStatusCode, data, error, message} = response as APIResponse;
console.log('add_item_to_cart_response:', response);
switch (httpStatusCode) {
case httpStatusCodes.SUCCESS_OK:
case httpStatusCodes.SUCCESS_CREATED:
dispatch(cartActions.updateCartLoadingStatus(false));
dispatch(cartActions.updateCartItemCount(quantity));
globalConfig.setCartItemCount(quantity);
break;
case httpStatusCodes.CLIENT_ERROR_UNAUTHORIZED:
dispatch(cartActions.updateCartLoadingStatus(false));
break;
case httpStatusCodes.SERVER_ERROR_INTERNAL_SERVER_ERROR:
case httpStatusCodes.CLIENT_ERROR_BAD_REQUEST:
dispatch(cartActions.updateCartLoadingStatus(false));
Alert.alert("Alert", (message)? message : "Error, it looks like you already have access to this course.", [
{
text: "Ok",
},
]);
break;
default: {
dispatch(cartActions.updateCartLoadingStatus(false));
}
}
};
};
Reducers.ts
const initialState: CartInitialState = {
isLoading: true,
cartToken: "",
responseStatus: apiResponseStatuses.IDLE,
cartItemCount: 0,
isMessageVisible: false,
message: "",
};
export default function cartReducer(
state = initialState,
action: CartActionTypes,
): CartInitialState {
switch (action.type) {
case UPDATE_LOADING_STATUS:
return {
...state,
isLoading: action.isLoading,
};
case UPDATE_CART_TOKEN:
return {
...state,
cartToken: action.cartToken,
};
case UPDATE_RESPONSE_STATUS:
return {
...state,
responseStatus: action.responseStatus,
};
case UPDATE_CART_ITEM_COUNT_TOKEN:
return {
...state,
cartItemCount: action.cartItemCount,
};
case CLEAR_DATA_ON_LOGOUT:
return {
...state,
isLoading: true,
cartToken: "",
responseStatus: apiResponseStatuses.IDLE,
cartItemCount: 0,
isMessageVisible: false,
message: "",
};
default: {
return state;
}
}
}
In the pruchaseItem() function of CourseSpecificScreen.tsx, I would like to dispatch a action that adds the item to the cart and immediately afterwards check if the cartItemCount has been updated & if it has, do something... This functionality works as expected, but only after clicking the Buy Now button twice.
I have ruled out the possibility of the issue being the API request failing the first time.
I have been stuck on this issue for several days now so any help or advice would be greatly appreciated. Let me know if I need to include more information
In my case, I was storing a reference of the old cartReducer state before it was being updated.
I got this working by updating my purchaseItem() function to look like the below:
pruchaseItem = async () => {
const {
courseSpecificReducer,
clearCartAndAddItem,
navigation
} = this.props;
const paymentMethod = paymentMethodForDevice();
await clearCartAndAddItem(
paymentMethod,
courseSpecificReducer.productData.code as string,
1,
navigation
)
const { cartReducer } = this.props;
if(cartReducer.cartItemCount > 0) {
// do some stuff
}
};

Reducer doesn't update state

I am trying to create a reducer to update a property in object, but i cant be able to update and store the new state information
Reducer
export default function hideCardNumber(state = INITIAL_STATE, action: Action) {
if (action.type === 'HIDE_CARDNUMBER') {
return {
...state,
data: {...state.data, action }}
}
else
return state
}
Action
export const toggleViewNumberCard = (cardId: number, hideCardNumber: boolean) => {
return {
type: 'HIDE_CARDNUMBER',
cardId,
hideCardNumber,
}
}
dispatch to action
function handleToggleViewCardNumber() {
cards.map((card: Card) => {
if (card.cardId === props.activeCard ) {
dispatch(toggleViewNumberCard(
card.cardId,
!card.hideCardNumber,
))
}
})
}
Initial State
export const INITIAL_STATE = {
activeCard: 0,
data: [
{
cardId: 0,
cardName: 'Card',
cardUsername: 'Name',
cardNumber: '1234 1234 1234 1234',
hideCardNumber: false, <-- Trying to replace this property when reducer update
},
]
}
You need to update reducer like this:
const {hideCardNumber, cardId} = action;
return {
...state,
data: state.data.map(item => item.cardId === cardId ? {...item, hideCardNumber} : item )
}
In real-world scenarios, cardID will be a hash. Also to easily maintain the store data when the application grows INITIAL_STATE should be like this.
export const INITIAL_STATE = {
activeCard: 0,
data: {
123456: {
cardId: 123456,
cardName: 'Card',
cardUsername: 'Name',
cardNumber: '1234 1234 1234 1234',
hideCardNumber: false,
},
}
}
Then the Reducer will be like this.
export default function hideCardNumber(state = INITIAL_STATE, action) {
if (action.type === 'HIDE_CARDNUMBER') {
return {
...state,
data: {
...state.data,
[action.cardId]: {
...state.data[action.cardId],
hideCardNumber: action.hideCardNumber
}
}
}
}
else
return state
}
If the activeCard matches one of the IDs in the cards, the code will work perfectly.
first of all it's better to put you data in payload like:
export const toggleViewNumberCard = (cardId: number, hideCardNumber: boolean) => {
return {
type: 'HIDE_CARDNUMBER',
payload : {
cardId,
hideCardNumber,
}
}
}
looks like you have array of cards ,first of all you must have find your current card that you wanna replace using cardid like this:
const index = state.data.findIndex(
(card) => cardId === action.payload.cardId
);
then copy your old array :
const newArray = [...state.data];
then replace that index of newarray with your new hideCardNumber value like this:
newArray[index] = {
...newArray[index],
action.payload.hideCardNumber
};
return {
...state,
data: newArray,
};
i hope it would help you

React redux map reducer

I have looked everywhere but nothing is working. I am trying to increment/decrement position based on which one they select on screen.
I am using useSelector to try and get a slice of the code.
const leagueSettings = useSelector((state) => state.drafts.rosterSettings);
But I cannot get the mapping to work. The state never updates. I can see that I am sending in an updated count for the position and the correct position.
My action.js
export const ADD_ROSTER_SPOT = "ADD_ROSTER_SPOT";
export const REMOVE_ROSTER_SPOT = "REMOVE_ROSTER_SPOT";
export const addRosterSpot = (position, count) => {
return {
type: ADD_ROSTER_SPOT,
position: position,
amount: count
};
};
export const removeRosterSpot = (position, count) => {
return {
type: REMOVE_ROSTER_SPOT,
position: position,
amount: count
};
};
my reducer.js
import { ADD_ROSTER_SPOT, REMOVE_ROSTER_SPOT } from "../actions/draft";
let initialState = {
leagueSettings: [], // list of league settings,
rosterSettings: [
{ Position: "QB", amount: 1 },
{ Position: "WR", amount: 1 },
{ Position: "RB", amount: 1 },
{ Position: "TE", amount: 1 },
],
drafted: [], // list of players drafted by others
};
export default (state = initialState, action) => {
switch (action.type) {
case ADD_ROSTER_SPOT:
return state.rosterSettings.map(pos => {
if (pos.Position === action.position) {
return {...pos, amount: pos.amount + 1}
}
console.log(state)
return pos;
});
case REMOVE_ROSTER_SPOT:
return state.rosterSettings.map((pos) => {
if (pos.Position === action.position) {
return { ...pos, amount: pos.amount - 1 };
}
return pos;
});
default: return state;
}
};
You need to change the code inside the reducer because it is not updating the state correctly
try using this in the "ADD_ROSTER_SPOT" case in reducer
let filteredState = state.rosterSettings.filter((eachPos) => {
if (eachPos.Position === action.Position) {
eachPos.amount = eachPos.amount + 1;
return eachPos;
}
return eachPos;
});
let newState = Object.assign({}, state, { rosterSettings: filteredState });
return({ ...newState });
Please check where the drafts reducer is defined, and what is your code in rootReducer?

how to save array object data in redux store

i try to store multiple object in redux store on my react native app, but only one object is save,
i'm new at redux, i try a lot of solutions found on StackOverflow but no one works :/
result i have in my store:
"hives": {"hive_id": 12944, "hive_name": null}
result i want (or something like that) :
"hives": [
1: {"hive_id": 123, "hive_name": "HelloHive"},
2: {"hive_id": 12944, "hive_name": null}]
store:
const middleware = [thunk]
export const store = createStore(persistedReducer, applyMiddleware(...middleware));
export const persistor = persistStore(store);
reducer :
const INIT_STATE = {
hives: [],
}
const hiveReducer = (state = INIT_STATE, action) => {
switch (action.type) {
case SET_HIVES:
return {
...state,
hives: action.payload,
};
[...]
action creator:
export const setHives = hives => {
return {
type: SET_HIVES,
payload: hives,
};
};
action:
export const getHives = () => {
return dispatch => {
axios.get(GET_HIVE_URL, HEADER).then(res => {
const status = res.data.status;
const hives = res.data.hives;
if (status == 'hiveFound') {
for (let i = 0; i < hives.length; i++) {
console.log(hives[i]);
dispatch(setHives(hives[i]));
}
}
});
};
};
and my API send me:
"hives": [
{
"hive_id": 123,
"hive_name": "HelloHive"
},
{
"hive_id": 12944,
"hive_name": null
}
]
and console.log(hives[i]) return :
LOG {"hive_id": 123, "hive_name": "HelloHive"}
LOG {"hive_id": 12944, "hive_name": null}
thanks you
First of all, in your reducer you don't need to use ...state spread operator, since hives seems to be the only one variable in your state there. And second, you are iterating over each element of hives, therefore you are inputting them one by one thus overwriting the previous one. You are not appending it to array. Here's how you need to change your action:
export const getHives = () => {
return dispatch => {
axios.get(GET_HIVE_URL, HEADER).then(res => {
const status = res.data.status;
const hives = res.data.hives;
if (status == 'hiveFound') {
dispatch(setHives(hives));
}
});
};
};
This way it will write the whole array into that variable in redux.
You can try this below so you can store the whole array. assuming you already have the actions.
InitialState
export default {
hives:[]
}
HivesReducer
export default function counter(state = initialState.hives, action) {
switch (action.type) {
case Types.SET_HIVES:
return [...state, action.payload];
default:
return state;
}
}
In your reducer try this :
case SET_HIVES:
return {
...state,
hives: [...state.hives,action.payload],
};
[...]
hope it helps. feel free for doubts

Resources