React setState variable issue - reactjs

I'm am using React to set the visbility (isMarkerShown) of a marker when I click a menu item which is handle by my handleMarker(). I get an undefined value if I tried to set the state like how I did below.
state = {
collapsed: false,
visible: false,
marker: {
isMarkerShown: false,
lat: 0,
lng: 0,
},
};
handleMarker() {
this.setState({marker: this.setState({isMarkerShown: true})});
console.log(this.state.marker);
}

It looks like you might be trying to maintain the rest of the marker props while changing isMarkerShown. If that's the case, the following approach may help:
handleMarker() {
this.setState({
marker: {
...this.state.marker,
isMarkerShown: true
}
});
}

setState function is a void function - it does not return anything. So actually if you are assigning it to some variable, it will hold an undefined value.
Just try to set the state directly:
this.setState((prevState) => ({
marker: {
...prevState.marker,
isMarkerShown: true,
},
});

If you need to access value of current/previous state while setting state- like toggling on/off- you can access it directly within the setState call. You can also use functional setState to ensure your state updates happen in order:
this.setState(prevState => ({ marker: { ...prevState.marker, isMarkerShown: !prevState.marker.isMarkerShown } }))

Related

Value is not written to this.state

On a React page I have a method by means of which I'm trying to set the state. However, it doesn't seem to work (see the console.log). What am I doing wrong?
editPlan = (cell, row) => {
return (
<img
src={edit}
alt="edit"
onClick={() => {
console.log(row.id); // prints 2
this.setState({ editId: row.id, openEditModal: true });
console.log(JSON.stringify(this.state.editId)); // prints "". I would expect 2.
console.log(JSON.stringify(this.state.openEditModal)); // prints false. I would expect true.
this.props.getPlan(row.id);
}}
/>
);
};
setState works asynchronously -- it just queues up your desired state change and will get to it later to optimize how your component will re-render.
You can pass a callback to the function as the last argument which will be called when the state change has occurred.
this.setState(
{ editId: row.id, openEditModal: true },
() => {
console.log(JSON.stringify(this.state.editId));
console.log(JSON.stringify(this.state.openEditModal));
}
);
See: https://reactjs.org/docs/react-component.html#setstate
You can try using the state callback function because setState works asynchronously
this.setState({ editId: row.id, openEditModal: true },()=>{
console.log(JSON.stringify(this.state.editId));
console.log(JSON.stringify(this.state.openEditModal));
this.props.getPlan(row.id);
});

how to create custom selector with local state

I need to create a custom selector that accepts redux state and local state to produce the result. But I don't know how to.
app.tsx
const initialConfig = {
storeType: {
[StoreType.CHAIN_STORE]: false,
[StoreType.PROSPECT]: false,
[StoreType.UNKNOWN]: false,
[StoreType.PRODUCT]: false
},
photo: {
binding: false
},
store: {
noUnit: false,
checkFlag: false,
newRepo: false,
notAssociated: false,
deletedAssociation: false
},
keyword: ''
};
//it's ui state (user's custom filter)
const copy: <T extends object>(source: T) => T = source =>
JSON.parse(JSON.stringify(source));
const [config, setConfig] = useState<Config>(copy(initialConfig));
const photos = copy(useSelector(photoWithUnitSelector).reduce(
filterFun(config),
[]
);
//each time render need to do a deep copy to ensure useSelector is not memorized.But it's a really perform issue.
photoWithUnitSelector = createSelector([A,B,C],(a,b,c)=>[...result])
//It is my selector that all data is from redux state.
filterFun = (config) => (prev,curr) => {
return my_own_fun_to_get_filtered_photos(config, prev, curr)
}
All I want is useSelector(selector(config),shallowEqual)
(combine redux state and local state to get my result)
But I don't know how to write the selector function.
Can someone give me a guide?

Action changes state of all values instead of just particular ones

I am trying to rewrite my application (weather check application) from standard React state approach into Redux based one. I encountered problem with changing state. In pure React by lanuching this.setState() I was changing only the piece I was passing as an argument in this method. In Redux it seems to change the entire state object even though I pass only part of the entire state, but probably I am doing something wrong.
Here you can see how state transforms when I trigger my action:
In my example I am trying to set coordinates states (lattitude and longtitude) when clicking the button. Tried several things - I thought that perhaps passing state = [] in reducer function clears out the entire state but I cannot get rid of it as application crashes.
I have 3 reducers: first for geolocation coords, second for weather details and last one for handling loading spinner to be displayed or not
const reducers = combineReducers({
weather: weatherReducer,
coords: coordsReducer,
loading: loadingReducer
})
Then I have store with initial states as following:
const store = createStore(reducers, {
weather: [{ temperature: "", humidity: "", windSpeed: "", pressure: "", pollution: "", date: "" }],
coords: { lat: null, lng: null },
loading: { loading: false }
}, allStoreEnhancers);
Not sure if splitting them all into 3 parts is good but I believe so (feel free to point out if I am wrong)
coordsReducer.js:
export default function weatherReducer(state = [], { type, payload }) {
switch (type) {
case 'updateCoords':
return payload
case 'clearCoords':
return payload
default:
return state
}
}
and coordsActions.js (simply set coords or clear them out)
export function updateCoords(lat = 0, lng = 0) {
return {
type: 'updateCoords',
payload: {
lat: lat,
lng: lng
}
}
}
export function clearCoords() {
return {
type: 'clearCoords',
payload: {
lat: null,
lng: null
}
}
}
Basically I need few things:
1. Opinion if the way I splitted my state and action and reducers makes sense
2. How to change state of single properties instead of changing entire state object?
This is because you have to pass in the previous state when you are setting new properties. You can do this easily with the spread operator.
export default function weatherReducer(state = [], { type, payload }) {
switch (type) {
case 'updateCoords':
return {
...state,
lat: payload.lat,
lng: payload. lng
}
case 'clearCoords':
return {
...state,
lat: null,
lng: null
}
default:
return state
}
}
and you can set the values you want to update individually.

How to setState() in loop using spread operator

I'm trying to set new values for several fields in nested object in state using spread operator in loop, but it works only for last field.
I have an array "formFields" with names of fields which values I want to overwrite. I use map() to compare each element in array with field in state and switch it's value to "true". But it change value only for the last field in array - "comment".
constructor() {
super();
this.state = {
fields: {
time: false,
date: false,
quantity: false,
comment: false,
},
}
}
getFormFields() {
const formFields = ["time", "quantity", "comment"];
formFields.map(item => {
this.setState({
...this.state.fields,
[item]: true
})
});
}
What should I do to overwrite values for all the fields I want?
Since you are changing state in a loop, and each state you set contains the original item with only changed, the latest change overrides the previous one. Instead, create a new state object with the change, and then setState the object once:
getFormFields() {
const formFields = ["time", "quantity", "comment"];
this.setState(formFields.reduce((r, item) => ({
...r,
[item]: true
}), {}));
}
btw - If the fields you want to set to true are always the same, you can create the object manually, and set it:
getFormFields() {
this.setState({
time: true,
quantity: true,
comment: true,
});
}

React JS - How to set state of variable inside variable?

I want to set state of this form :
this.state = {
filter: {
search: null,
brands: null,
price: null
}
}
How to set value for search / brands / price ?
Do the following:
this.setState({
filter: {
search: 'value',
brands: 'value',
price: 'value'
}
})
The key is that you don't want to ever mutate a value in state. As a result, you must copy the filter object before passing it to setState. Example:
onSearchChange(value) {
this.setState((state) => {
return {
filter: {
...state.filter,
search: value
}
})
}
Note that I am passing a function to setState. Since the next value of state relies on the previous value, you want to use an updater functions, as the setState docs recommend.
In general, it is nicer if you can keep your state flat. So rather than having a filter object in state, your shape could just be
this.state = {
search: null,
brands: null,
price: null,
}
In which case the above onSearchChange function would just be
onSearchChange(value) {
this.setState({search: value})
}
Definitely a lot easier on the eyes.
I recommend avoiding nested objects and keeping your state flat. e.g.
this.state = {
brandsFilter: null,
priceFilter: null,
searchFilter: null,
};
Component state in react is really nothing more than simple key-value pairs; that's what setState supports. Sure you can have nested objects if you really want. But as the other answers demonstrate, supporting such an approach can be needlessly complex.
you should use the setState function, you can set the filter with updated data like so
const newFilter = {...};
this.setState({filter: newFilter});
You should avoid to mutate React state directly, there are some functions can do immutable jobs for you (ex Object.assign):
const currentFilter = this.state.filter;
const newFilter = Object.assign({}, currentFilter, {search: "new search", brands: [], price: 100});
this.setState({filter: newFilter});
ES 6:
const currentFilter = this.state.filter;
this.setState({
filter: {
...currentFilter,
search: "new search",
brands: []
}
});
this.setState({
filter: {
...this.state.filter,
search: "new search",
brands: 'value',
price: 'value'
}
});
You may try this with Spread Operator.
If you need to preserve the previous filter value.
other wise you can,
this.setState({
filter: {
search: "new search",
brands: 'value',
price: 'value'
}
});
let newfilter = Object.assign({}, this.state.filter)
newfilter.search ="value";
newfilter.brands ="value";
newfilter.price ="value";
this.setState({
filter:newfilter
})
You can access the search, brands, price by using:
this.setState({
filter.search = true,
filter.brands = 'stackoverflow',
filter.price = 1400
})
and to access it, just like usual state access (this.state.filter.search).

Resources