(反应)选择不同的下拉值不会改变任何东西

(React) Selecing different dropdown values does not change anything

我有一个下拉菜单,选择不同的值不会改变任何东西。以前,它确实改变得很好。这是我的代码:

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

============================================= =====================

如你所见,只有当我添加

setState({
            ...state,
            sortedRooms: tempRooms
        })

in filterRooms (in context.js) 我无法再更改下拉值。为什么添加 setState 会这样做?

请多多指教,感谢阅读!

===============更新========= ============

  1. RoomsContainer 是包含下拉菜单 (RoomsFilter) 和该下拉菜单下方的房间列表 (RoomsList) 的容器。
  2. RoomsFilter 是下拉菜单组件
  3. RoomsList 是负责渲染房间的组件

房间容器:

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)

房间过滤器:

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

房间列表

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

您没有正确使用 setState ..应该这样使用

setState(prevState => ({...prevState , sortedRooms: tempRooms}))

问题是您有两个不同的 setState 调用;一个在您的 handleChange 函数中,另一个在 filterRooms 中。给定更改的这两个函数都使用以前的状态,但在第一个之后:

setState({
  ...state,
  [name]: value 
})

...state 在您调用时仍将具有相同的原始值:

setState({
  ...state,
  sortedRooms: tempRooms
})

...所以第二次更新将覆盖第一次 setState 所做的。正如已经指出的那样,您可以在第二次调用中使用 setState 的回调版本,这样您就可以在第二次调用中使用 new 先前的状态:

setState(state => ({
  ...state,
  sortedRooms: tempRooms
}));

...但是 更好的 方法是只调用 setState 一次 而不是混淆两个状态更新。而不是 filterRooms 调用 setState,让它 return 过滤数组,并在一次调用中完成所有更新。以下是 filterRooms 可以做的事情:

const getFilteredRooms = (type) => {
  let { rooms, capacity, price, minSize, maxSize, breakfast, pets } = state
  return type === 'all' ? rooms : rooms.filter(room => room.type === type)
}

...并将您的 handleChange 更改为:

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