Reputation: 201
I have a dropdown menu, and selecting different values does not change anything. Previously, it did change just fine. Here is my code:
context.js:
import React, { useState, useEffect } from 'react'
import items from './data'
const RoomContext = React.createContext()
const RoomProvider = ({ children }) => {
const [state, setState] = useState({
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
}
const getRoom = slug => {
let tempRooms = [...state.rooms]
const room = tempRooms.find(room => room.slug === slug)
return room
}
useEffect(() => {
let rooms = formatData(items)
let featuredRoomsTemp = state.rooms.filter(room => room.featured)
let maxPrice = Math.max(...state.rooms.map(item => item.price))
let maxSize = Math.max(...state.rooms.map(item => item.size))
setState({
...state,
rooms,
featuredRooms: featuredRoomsTemp,
sortedRooms: rooms,
loading: false,
price: maxPrice,
maxPrice,
maxSize
})
}, [])
const handleChange = e => {
const target = e.target
const value = e.type === 'checkbox' ? target.checked : target.value
const name = e.target.name
setState({
...state,
[name]: value
})
filterRooms()
}
const filterRooms = () => {
console.log('reached')
let { rooms, type, capacity, price, minSize, maxSize, breakfast, pets } = state
let tempRooms = [...rooms]
if (type !== 'all') {
// console.log('NOT ALL')
tempRooms = tempRooms.filter(room => room.type === type)
}
console.log('temprooms:' ,tempRooms)
//ONLY AFTER ADDING THE BELOW SETSTATE, AM I UNABLE TO CHANGE THE DROPDOWN VALUES. IF I REMOVE THE BELOW, I CAN CHANGE IT JUST FINE. Why would a setState make me unable to change the values?
setState({
...state,
sortedRooms: tempRooms
})
}
return (
<RoomContext.Provider value={{
...state,
getRoom,
handleChange
}}>
{children}
</RoomContext.Provider>
)
}
const RoomConsumer = RoomContext.Consumer
export function withRoomConsumer(Component) {
return function ConsumerWrapper(props) {
return <RoomConsumer>
{value => <Component {...props} context={value} />}
</RoomConsumer>
}
}
export { RoomProvider, RoomConsumer, RoomContext }
RoomsFilter.js
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]))]
}
const RoomsFilter = ({ rooms }) => {
const context = useContext(RoomContext)
const { handleChange, type, capacity, price, minPrice, maxPrice, minSize, maxSize, breakfast, pets } = context
let types = getUnique(rooms, 'type')
types = ['all', ...types]
types = types.map((item, i) => {
return (
<option value={item} key={i}>{item}</option>
)
})
return (
<section className='filter-container'>
<Title title='Search rooms' />
<form className='filter-form'>
{/* select type */}
<div className='form-group'>
<label htmlFor='type'>Room type</label>
<select name='type' id='type' value={type} className='form-control' onChange={handleChange}>
{types}
</select>
</div>
{/* end select type */}
</form>
</section>
)
}
export default RoomsFilter
==================================================================
As you can see, it is only when I added
setState({
...state,
sortedRooms: tempRooms
})
in filterRooms (in context.js) that I am no longer able to change the dropdown value. Why would adding setState do this?
Please help, and thank you for reading!
===============UPDATE=====================
Rooms Container:
import React from 'react'
import RoomsFilter from './RoomsFilter'
import RoomsList from './RoomsList'
import { withRoomConsumer } from '../context'
import Loading from './Loading'
function RoomContainer({ context }) {
const { loading, sortedRooms, rooms } = context
console.log(context)
if (loading) {
return <Loading />
}
return (
<div>
<RoomsFilter rooms={rooms} />
<RoomsList rooms={sortedRooms} />
</div>
)
}
export default withRoomConsumer(RoomContainer)
RoomsFilter:
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]))]
}
const RoomsFilter = ({ rooms }) => {
const context = useContext(RoomContext)
const { handleChange, type, capacity, price, minPrice, maxPrice, minSize, maxSize, breakfast, pets } = context
let types = getUnique(rooms, 'type')
types = ['all', ...types]
types = types.map((item, i) => {
return (
<option value={item} key={i}>{item}</option>
)
})
return (
<section className='filter-container'>
<Title title='Search rooms' />
<form className='filter-form'>
{/* select type */}
<div className='form-group'>
<label htmlFor='type'>Room type</label>
<select name='type' id='type' value={type} className='form-control' onChange={handleChange}>
{types}
</select>
</div>
{/* end select type */}
</form>
</section>
)
}
export default RoomsFilter
RoomsList
import React from 'react'
import Room from './Room'
const RoomsList = ({ rooms }) => {
console.log(rooms)
if (rooms.length === 0) {
return (
<div className='empty-search'>
<h3>Unfortunately no rooms match your search criteria</h3>
</div>
)
}
return (
<section className='roomslist'>
<div className='roomslist-center'>
{rooms.map(item => {
return <Room key={item.id} room={item} />
})}
</div>
</section>
)
}
export default RoomsList
Upvotes: 0
Views: 649
Reputation: 819
You are not using the setState correctly .. it should be used like this instead
setState(prevState => ({...prevState , sortedRooms: tempRooms}))
Upvotes: 0
Reputation: 78890
The problem is that you have two different setState
calls; one is in your handleChange
function, and the other is inside of filterRooms
. Both of those functions on a given change are using the previous state, but after the first:
setState({
...state,
[name]: value
})
...state
is still going to have that same original value when you call:
setState({
...state,
sortedRooms: tempRooms
})
...so the second update is going to overwrite what the first setState
did. As was already pointed out, you can use the callback version of setState for the second call so you're using the new previous state on that second call:
setState(state => ({
...state,
sortedRooms: tempRooms
}));
...but a better approach is to only call setState
once on change instead of confusingly making two state updates. Instead of filterRooms
calling setState
, have it return the filtered Array, and do your update all in one call. Here's what filterRooms could do instead:
const getFilteredRooms = (type) => {
let { rooms, capacity, price, minSize, maxSize, breakfast, pets } = state
return type === 'all' ? rooms : rooms.filter(room => room.type === type)
}
...and change your handleChange
to:
const handleChange = e => {
const target = e.target
const value = e.type === 'checkbox' ? target.checked : target.value
const name = e.target.name
setState({
...state,
[name]: value,
sortedRooms: getFilteredRooms(value)
})
}
Upvotes: 1