update values in the Reducer in Redux - reactjs

i got two values i.e.company and id from navigation.
let id = props.route.params.oved;
console.log("id-->",id);
let company = props.route.params.company;
console.log("company--->",company);
i got two values as a integer like this:--
id-->1
comapny-->465
Description of the image:---
if i am giving input 1 in that textInput and click on the card(lets say first card i.e.465 then i am getting those two values in navigation as in interger that i have mention above.so each time i am getting updated values.
i am getting updated values from navigation.
so i want to store those values in redux.
action.js:--
import { CHANGE_SELECTED_COMPANY } from "./action-constants";
export const changeCompany = (updatedCompany, updatedId) => {
return {
type: CHANGE_SELECTED_COMPANY,
updatedCompany,
updatedId,
};
};
reducer.js:--
import { CHANGE_SELECTED_COMPANY } from "../actions/action-constants";
const initialState = {
company: "",
id: "",
};
const changeCompanyReducer = (state = initialState, action) => {
switch (action.type) {
case CHANGE_SELECTED_COMPANY:
return {
company: {
company: action.updatedCompany,
id: action.updatedId,
},
};
}
return state;
};
export default changeCompanyReducer;
congigure-store.js:--
import changeCompanyReducer from "./reducers/change-company-reducer";
const rootReducer = combineReducers({changeCompanyReducer});
How can i store the update values getting from navigation in Redux?
could you please write code for redux??

in the component create a function that updates the values
const updateReducer = () => {
dispatch(changeCompany(props.route.params.oved, props.route.params.company))
}
then call the function in react navigation lifecycle event
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
updateReducer()
});
return unsubscribe;
}, [navigation])
its possible that a better solution would be to update the reducer before the navigation happens and not pass the data in the params but rather pull it from redux but this is the answer to the question as asked

Related

how to override state Redux

I don't understand something in react-redux.
I have created a slice called Introduction look below:
import { createSlice } from "#reduxjs/toolkit";
import { IntroductionFields } from "../helpers/interface";
const initialState: IntroductionFields = {
fullName:'',
subtitle:'',
description:'',
location:'',
email:'',
portfolio: {name:'' , url:''},
project: {name: '' , url: ''},
learning: '',
collaborating: '',
else: '',
}
const Introduction = createSlice({
name: 'intro',
initialState,
reducers:{
update(state, actions){
const key = actions.payload.name;
const val = actions.payload.value;
state.fullName = val; // WORK
state = {...state, [key]: val} // NO WORK
console.log(actions.payload.name , " " , actions.payload.value);
},
}
})
export const IntroductionActions = Introduction.actions;
export default Introduction;
and I have two more components,
first component has fields (inputs) and every field has an onChange that calls the dispatch and uses update on the reducer that I created in the introduction slice and I send the key and value, see below.
const Intro: React.FC<Props> = ({ moveForward }) => {
const dispatch = useDispatch();
const changeHandler = (event: React.ChangeEvent<HTMLInputElement> | React.ChangeEvent<HTMLTextAreaElement>) => {
const {name , value} = event.target;
dispatch(IntroductionActions.update({name, value}))
}
return (.... // HERE I HAVE INPUTS...)
}
In the second component I want to get the values from the Introduction slice so if I change some fields in Intro component, I want to see the changes in my Preview component.
import React, { useEffect } from 'react'
import classes from './Preview.module.scss';
import { useSelector } from 'react-redux';
import { RootState } from '../../../store/store';
const Preview = () => {
const introduction = useSelector((state:RootState) => state.intro);
return (
<div className={classes.previewContainer}>
{introduction.fullName && <h1>Hi! My name is {introduction.fullName}</h1>}
</div>
)
}
export default Preview
If you'll look to the first code section
you will see these two lines.
state.fullName = val; // WORK
state = {...state, [key]: val} // NO WORK
If I directly write into the field in state it works prefect, but if I try to do the second line it doesn't work...
I want it to be dynamic that is why I want to use the second line...
You can set the state like this since there is no need to copy the whole state to the new state.
update(state, actions){
const key = actions.payload.name;
const val = actions.payload.value;
state[key] = val;
},
The section Create a Redux State Slice will explain in depth how/why
Dispatch the action with object as payload
dispatch(IntroductionActions.update({fullName: name, subtitle: subtitle}))
and your reducer function will be like this
update(state, actions){
return ({...state, ...actions.payload})
}
Here based on the payload, state will get updated , here fullName and subtitle values will get updated.

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
};
}

Use Context to update data on every screen

This is my first time working with context. So, I've about 6 screens and on each screen, the user selects some options and on the 6th screen, I want all the information that they have selected/entered on previous screens.
I've created a class called context.js in which I've done this:
export const bookData = React.createContext({
bookingData: {
"app_date": 0,
"app_est": "",
"app_phone": "",
"app_ic": "",
"app_timeslots": {},
"app_hour": 0,
"app_est_url": "",
"app_year": 0,
"app_email": "",
}
})
This is the payload I'll need on the 6th screen. Now on-screen 1 I've imported it as import BookContext from '../utils/context' and this screen has a next button in which I want to pass data to app_email, app_phone, and app_ic which I've in variables that I populated using. useState() on this screen:
<MainActionButton title={'Next'} pressEvent={() => {
alert(email + phone + ic)
}} />
Here email, phone, and ic are set using a useState(). So how do I use BookContext that I've imported inside the pressEvent to access/update the context?
I might be completely off track here as I don't fully understand the concept of context yet so a brief answer will be appericiated.
For updating the value of the context you can follow this simple pattern:
Maintain a state in the component that has the provider. For example, you can have something like this:
const initialState = {
bookingData: {
"app_date": 0,
"app_est": "",
"app_phone": "",
"app_ic": "",
"app_timeslots": {},
"app_hour": 0,
"app_est_url": "",
"app_year": 0,
"app_email": "",
}
};
const [bookData, setBookData] = react.useState(initialState);
Pass the bookData and setBookData as context's value:
<BookData.Provider value={{state: bookData, updateState: setBookData}}></BookData.Provider>
Now in the child components you can use the passed object(value) for accessing and updating the state.
const { state, updateState } = React.useContext(BookData)
you would need to create an "api" inside your context to update records (e.g. bookContext.update(payload)), for example:
import * as React from "react";
let ContextOne = React.createContext();
let initialState = {
count: 10,
currentColor: "#bada55"
};
let reducer = (state, action) => {
switch (action.type) {
case "reset":
return initialState;
case "increment":
return { ...state, count: state.count + 1 };
case "decrement":
return { ...state, count: state.count - 1 };
case "set-color":
return { ...state, currentColor: action.payload };
}
};
function ContextOneProvider(props) {
let [state, dispatch] = React.useReducer(reducer, initialState);
let value = { state, dispatch };
return (
<ContextOne.Provider value={value}>{props.children}</ContextOne.Provider>
);
}
let ContextOneConsumer = ContextOne.Consumer;
export { ContextOne, ContextOneProvider, ContextOneConsumer };
i wrap my app in my custom context/provider:
import { ContextOneProvider } from "./ContextOne";
import { App } from "./App";
ReactDOM.render(
<ContextOneProvider>
<App />
</ContextOneProvider>,
document.getElementById("root")
);
now when I consume ContextOne:
function App() {
let { state, dispatch } = React.useContext(ContextOne);
// dispatch will update the context and re-render
}
I wrote a walkthrough about that in:
https://dev.to/oieduardorabelo/react-hooks-how-to-create-and-update-contextprovider-1f68
One library that I recommend to abstract React.Context is:
https://github.com/jamiebuilds/unstated-next

i could not edit data by using react-redux

I have an issue with update new data by using react-redux, Add and remove are working fine, but it will return null object when i want to edit one of the data.
I am not sure what stage cause wrong.
action.users.js
import { v4 as uuid } from 'uuid';
// ADD_USER
export const addUser = ({ username = '', location = '' } = {}) => ({
type: 'ADD_USER',
user: { id: uuid(), username, location },
});
//REMOVE_USER
export const removeUser = ({ id } = {}) => ({ type: 'REMOVE_USER', id });
//EDIT_USER
export const editUser = ({ id, updates } = {}) => ({
type: 'EDIT_USER',
id,
updates,
});
components.EditUserPage.js
import React from 'react';
import { connect } from 'react-redux';
import UserForm from './UserForm';
import { editUser } from '../actions/users';
const EditUserPage = props => {
return (
<div>
<UserForm
user={props.user}
onSubmit={user => {
props.dispatch(editUser(props.user.id, user));
props.history.push('/Users');
}}
/>
</div>
);
};
const mapStateToProps = (state, props) => {
return {
user: state.user.find(user => user.id === props.match.params.id),
};
};
export default connect(mapStateToProps)(EditUserPage);
reducers.users.js
const usersReducerDefaultState = [];
export default (state = usersReducerDefaultState, action) => {
switch (action.type) {
case 'ADD_USER':
return [...state, action.user];
case 'REMOVE_USER':
return state.filter(({ id }) => id !== action.id);
case 'EDIT_USER':
return state.map(user => {
if (user.id === action.id) {
return {
...user,
...action.updates,
};
} else {
return user;
}
});
default:
return state;
}
};
The page should go to ./edit when i click one of the data and the input value will show the currently selected data in the userform component. it seems like going well at this stage, i change the input value and click the create button, the page back to /User, unfortunately, the selected data return null object. please help me. you answer will help me to jump out of this nightmare.
Change
props.dispatch(editUser(props.user.id, user));
to
props.dispatch(editUser({id: props.user.id, updates: user}));
You define the method signature of edit user as
function editUser ({ id, updates } = {}) {…}
That is, a function that takes one (optional) argument. That argument is expected to be an object with an id and updates property. (I don't think you should make the argument optional. It is needed for the rest of the function to work.)
However, you call the function with two arguments, presumable a number and an object. Also, if props.user.id ends up being undefined, then it will be replaced with the default value of {}, and id and updates will be undefined, but no error will occur (which is what you want, because you are passing the wrong type to the function).
Alternatively, you could define the method signature to take to positional arguments and not change the function call:
const editUser = (id, updates) => (…)
or, if your UserForm component includes id in the call to onSubmit:
const editUser = ({ id, ...updates }) => (…) // use destructuring to select the id from the argument
// call it like this:
props.dispatch(editUser({user}));

Filter function is not working in react/redux

I am newbie in react/redux and I try to do an app to manage my sentences.
I try to filter my sentences and it does it correct but when i delete some words from the input field, my state remains filtered.
this is the action.
export const filterWord = (e) => {
return {
type: 'FILTER_WORD',
e
}
}
this is the reducer:
case 'FILTER_WORD':
return state.filter((sentence) => sentence.word.includes(action.e.target.value));
Hope someone can give me an advice
The best way to handling filtering is not filter your reducer state based on the filter word but to do that in a selector
If you filter your state in a reducer, the next time you change a filter or remove it, you would not have the original state but a duplicate state
You can instead store the filterword in reducer
export const filterWord = (e) => {
return {
type: 'FILTER_WORD',
word: e.target.value,
}
}
const reducer = (state = { data: [], filterWord: null}) => {
...
case 'FILTER_WORD':
return {...state, filterWord: action.word};
...
}
Now in your mapStateToProps component you can filter the result for the user component
const mapStateToProps = (state) => {
return {
data: state.data.filter((sentence) => sentence.word.includes(state.filterWord));
}
}

Resources