I have a function handleChange, which gets triggered by a dropdown menu, it adds a prop of "type" to the state obj, with an appropriate value. The useStateWithCallBackLazy is just a custom hook taken from a third party library to imitate the same features as of the previous class based setState function.
I think I am not understanding now rendering or state works in a fundamental level.
The function FilterRooms is supposed to read the prop "type" and filter an array obtained from state and update the prop "sortedRooms", to the the filtered list.
Now if I call filterRooms() inside handleChange, the state does not seem to have the property "type", and when I click it again, it has the type of previous selection.
Meaning the filterRooms function is getting called before state is being updated. How can I solve this problem without it triggering a re-render infinite times
But I actually console.logged from a component that receives the state via context, now the state is updated there!
But it's not getting updated in my filterRoom function. How can I fix that? It doesn't even get the updated state after doing a setTimeout for 10s.
Here is the screenshot of my console.log:
https://i.stack.imgur.com/c2vZD.jpg
P.S : the suggested post does not solve my problem as the function I would need to write inside an useEffect also mutates the state, hence causing an infinite render. Thus I need an alternate solution.
import React, {useEffect, useState } from 'react'
import {useStateWithCallbackLazy } from 'use-state-with-callback'
import items from './data'
const RoomContext = React.createContext();
const RoomProvider = ({children}) => {
const [state, setState] = useStateWithCallbackLazy({
rooms: [],
sortedRooms: [],
featuredRooms: [],
loading: true,
type: "all",
capacity: 1,
price: 0,
minPrice: 0,
maxPrice: 0,
minSize: 0,
maxSize: 0,
breakfast: false,
pets: false
})
const formatData =(items) => {
let tempItems = items.map((item)=> {
let id = item.sys.id;
let images = item.fields.images.map(image => image.fields.file.url);
let room= {...item.fields, images,id};
return room;
});
return tempItems;
}
useEffect(()=> {
let rooms= formatData(items);
let featuredRooms = rooms.filter(room => room.featured === true)
let maxPrice = Math.max(...rooms.map((item)=> item.price))
let maxSize = Math.max(...rooms.map((item)=> item.size))
setState({
rooms,
sortedRooms: rooms,
featuredRooms,
loading: false,
price: maxPrice,
maxPrice: maxPrice,
maxSize: maxSize
})
console.log("render")
},[setState])
const handleChange= async(e)=> {
console.log("EVENT TARGET", e.target, e.target.value)
const target = e.target;
const value= e.type === 'checkbox'? target.checked : target.value ;
const name = e.target.name;
// const value = e.target.type;
// this.setState({
// [name]: value
// }, filterRooms) //filterRooms is a function
console.log("VALUE", value)
//-------------------------------LOOK---------HERE-------------------------
setState(state=> {
return {
...state,
[name]: value,
}
}, filterRooms)
console.log("apparent",state)
//----------------------------------THE ABOVE LOG DOESNT GET UPDATED STATE-----
}
// 04:18:38
//MY--------FILTERROOMS--------FUNCTION------------------------------
let filterRooms = ()=> {
console.log("filterRoom render", state)
let {
rooms,
type,
capacity,
price,
minSize,
maxSize,
breakfast,
pets
}= state // MY STATE ISN'T UPDATING HERE---------------------------
let tempRooms = [...rooms];
if(type!== 'all'){
let tempItems = tempRooms.filter(room => room.type === type)
console.log("MEEEEEEEE",type, tempItems)
setState(state => (
{
...state,
sortedRooms: tempItems
}
))
}
}
const getRoom = (slug)=> {
let tempRooms = [...state.rooms]
const room = tempRooms.find(room => room.slug === slug)
return room;
}
return (
<RoomContext.Provider value={{state,setState,getRoom, handleChange}}>
{children}
</RoomContext.Provider>
)
}
const RoomConsumer = RoomContext.Consumer;
export function withRoomConsumer(Component){
return function ConsumerWithWrapper(props){
return <RoomConsumer>
{ value => <Component {...props} context={value} /> }
</RoomConsumer>
}
}
export { RoomContext, RoomProvider, RoomConsumer };
My component that receives the state on time
import React, {useContext} from 'react'
import { RoomContext } from '../context';
import Title from './Title';
const getUnique = (items, value)=> {
return [...new Set(items.map(item=> item[value])) ]
}
The component RoomsFilter which receives the state via context.
const RoomsFilter = ({rooms}) => {
const context = useContext(RoomContext) ;
const {
state,
handleChange,
} = context
const {
type,
capacity,
price,
minPrice,
maxPrice,
minSize,
maxSize,
breakfast,
pets
} = state;
let types= getUnique(rooms,'type');
types= ['all', ...types]
types = types.map((item,index)=> (
<option value={item} key = {index}>{item}</option>
))
//----------------UPDATED---STATE---HERE-WHAT---I---WANT-IT---TO--LOOK-LIKE
console.log(state);
return (
<section className="filter-container">
<Title title="search-rooms" />
<form className="filter-form">
{}
<div className='form-group'>
<label htmlFor='type'>Room Type</label>
<select
name="type"
id="type"
value={type}
onChange={handleChange}
className="form-control"
>
{types}
</select>
</div>
{}
</form>
</section>
)
}
export default RoomsFilter;