Zustand state with javascript Map is not updating - javascript

I am trying to use map to store state, while the state is updating, the components are not re-rendering with the new state
const store = (set, get) => ({
items: new Map(),
addItem: (key, item) => {
set((state) => state.items.set(key, item));
// this prints the latest state
console.log(get().items)
},
removeItem: (key) => {
set((state) => state.items.delete(key));
},
)}
const useStore = create(store);
export default useStore
useEffect doesn't prints anything
const items = useStore((state) => state.items))
useEffect(() => {
console.log(items)
}, [items]);

You should create a new instance of Map when you update like so:
const store = (set, get) => ({
items: new Map(),
addItem: (key, item) => {
set((state) => {
const updatedItems = new Map(state.items)
updatedItems.set(key, item)
return { items: updatedItems }
});
// this prints the latest state
console.log(get().items)
},
)}
Note: you'll have to do something similar when you remove items.

The Map instance is always the same. So Zustand cannot detect any changes. You can use an array:
const store = (set, get) => ({
items: [],
addItem: (key, item) => {
set((state) => ({
items: [...state.items, { key, item }]
}));
},
removeItem: (key) => {
set((state) => ({
items: state.items.filter((item) => item.key !== key)
}));
}
});

If you don't want to create a new instance of a map so that zustand registers state change then change you can use immer.
import create from 'zustand'
import { immer } from 'zustand/middleware/immer'
const store =immer( (set, get) => ({
items: new Map(),
addItem: (key, item) => {
set((state) => state.items.set(key, item));
// this prints the latest state
console.log(get().items)
},
removeItem: (key) => {
set((state) => state.items.delete(key));
}),
)}
const useStore = create(store);
export default useStore

Related

How to pass object value as key of setState in react

I want to pass object value dynamically as key of setState.
useEffect(() => {
inputFields.forEach((item) => {
return setState({ ...state, [item.name]: "" });
});
}, [])
You can clone, assign and then setstate
useEffect(() => {
const sItem = {...state };
inputFields.forEach((item) => {
sItem[item.name] = "";
});
setState(sItem)
}, [])

How to save multiple data in localStorage with zustand?

I build a stepper and I want save my data in each step and access all my last data`s
export const useInformation = create(
persist(
(set) => ({
personInfo: [],
setPersonInfo: (callBack) => {
set((state) => ({
personInfo: callBack,
}));
},
}),
{
name: 'Person_setting',
getStorage: () => localStorage,
}
)
);
this my store with zustand and it will update in my component with onclick but when i update it, its return just last data steps
but

Component keeps re-rendering after state change

I use handleMouseOver and handleMouseOut functions where I change the value of a state count. However, every time the state is changed the component re-renders instead of just the state. What am I missing here? Thanks.
function foo() {
const [state, setState] = useState({ count: 0, data: {}});
useEffect(() => {
const getData = async () => {
const response = await fetch(url);
const data = await response.json();
setState(prevState => ({
...prevState,
data: data,
}));
};
return ()=>{
getData();
}
}, []);
function handleMouseOver(e) {
setState(prevState => ({
...prevState,
count: e,
}));
};
function handleMouseLeave() {
setState(prevState => ({
...prevState,
count: null,
}));
};
const { count, data } = state;
const BlockComponent = () => {
const data = data.arr;
return (
<Wrapper >
{data.map((value, index) =>
value.map((value, index) => {
return (
<Block
key={index}
val={value}
onMouseEnter={e => handleMouseOver(value)}
onMouseOut={handleMouseLeave}
></Block>
);
})
)}
</Wrapper>
);
};
return (
<Wrapper>
<BlockComponent />
</Wrapper>
);
}
export default foo;
The Issue is with your handleMouseOver function. It is getting executed everytime there is a state Update and the same value is assigned to "count".
All you have to do is place setState inside the condition that will compare the value of event received by the function and the current value of sate.
It should be something like this.
function handleMouseOver(e) {
if (count !== e) {
setState((prevState) => ({
...prevState,
count: e,
}));
}
}
React updates component if component state is changed. That's correct behaviour.
I recommend you to learn react documentation, because component state is a basic concept.
That's one of the main points of state -> component is rerendered when you change state.

How to delete a specific item from localstorage in react redux

How can I remove a specific item (by id) from localstorage using react (redux - persist)? handleSubmit is working fine, but handleDelete, is not. I have this:
handleSubmit = event => {
event.preventDefault();
this.props.addWeather(this.state.weatherCity);
this.setState({ weatherCity: "" });
};
handleDelete = (event, id) => {
this.props.deleteWeather(this.state.weatherCity);
this.setState({ weatherCity: "" });
}
const mapStateToProps = state => ({
allWeather: state.allWeather
});
const mapDispatchToProps = dispatch =>
bindActionCreators(WeatherActions, dispatch);
export default connect(mapStateToProps, mapDispatchToProps)(WeatherList);
And button in form to call handleDelete:
<form onSubmit={this.handleDelete}><button type="submit" id="add" onClick={this.handleDelete}>Remove City</button></form>
My localstorage:
allWeather: "[{\"id\":0.5927975642362653,\"city\":\"Toronto\"},{\"id\":0.8124764603718682,\"city\":\"Fortaleza\"},{\"id\":0.9699736666575081,\"city\":\"Porto\"},{\"id\":0.852871998478355,\"city\":\"Tokio\"},{\"id\":0.8854642571682461,\"city\":\"New York\"}]"
My reducer:
export default function allWeather(state = [], action) {
switch (action.type) {
case "ADD_WEATHER":
return [...state, { id: Math.random(), city: action.payload.city }];
case "DELETE_ITEM":
return [...state, state.weatherCity.filter((event, id) => id !== action.payload.id)];
default:
return state;
}
}
And actions:
export const deleteWeather = id => ({
type: "DELETE_ITEM",
payload: { id }
});
I appreciate any help.
Your problem is that you are using the spread operator, which copies the content of the current state first. Then you are adding the items that were returned from the filter method. So you aren't deleting but adding. To delete from an array use the filter method only, without the spread operator like that:
return state.filter( (city) => city.id !== action.payload.id )
Also the state is an array, not an object, so this is invalid state.weatherCity.

React Hooks useState Array empty with states rendered in the component

I have a situation where i can successfully dispatch my states with reducers and i can render it in my component
Here the relevant code
in my action/index.js
export const receivedLeaguesList = json => ({
type: RECEIVE_LEAGUES_LIST,
json: json
});
export function fetchLeaguesList() {
return function(dispatch) {
dispatch(requestLeaguesList());
return axios
.get("https://www.api-football.com/demo/v2/leagues/")
.then(res => {
let leagues = res.data.api.leagues;
dispatch(receivedLeaguesList(leagues));
})
.catch(e => {
console.log(e);
});
}
}
my reducers/index.js
import { REQUEST_LEAGUES_LIST, RECEIVE_LEAGUES_LIST } from "../actions";
const initialState = {
leaguesList: [],
isLeagueListLoading: false
};
const reducer = (state = initialState, action) => {
switch (action.type) {
case REQUEST_LEAGUES_LIST:
return { ...state, isLeagueListLoading: true };
case RECEIVE_LEAGUES_LIST:
return { ...state, leaguesList: action.json, isLeagueListLoading: false };
default:
return state;
}
};
in my component component/Leagues.js
let Leagues = ({ leaguesList, loading, getList }) => {
useEffect(() => {
getList();
}, [getList]);
const [itemsLeagues] = useState([leaguesList]);
console.log("league list", itemsLeagues);
const mapDispatchToProps = {
getList: fetchLeaguesList
};
I have reproduced the demo here => https://codesandbox.io/s/select-demo-71u7h?
I can render my leaguesList states in my component doing the map, but why when
const [itemsLeagues] = useState([leaguesList]);
console.log("league list", itemsLeagues);
returns an empty array ?
See the image
You're setting useState's init value wrong:
const [itemsLeagues] = useState(leaguesList);
instead of
const [itemsLeagues] = useState([leaguesList]);
The return value of useState isn't the value itself, but the array of value and mutator:
const [value, setValue] = useState([42, 43])
// here's value equals [42, 43]
So if you were trying to destructure the wrapping array you passed to useState(), you should use it like this (though you don't need it):
const [[itemsLeagues]] = useState([leaguesList]);

Categories