发送组件新道具反应 - 卡住

Send component new props react - Stuck

我已经被困了几个小时了..我正在尝试发送一个组件新的道具...但是它不会得到新的。

事情是这样的。

首先,用户单击 post 组件上的按钮...它会触发下方 parent 组件上的 'add favorites' 和 'addFavs' 功能。这两个函数决定是否应该将用户添加到收藏夹或从收藏夹中删除用户。

当用户被添加为收藏夹时,他们在 searchResults 组件中单击的按钮会显示不同的颜色。问题是他们必须单击按钮两次才能将新道具发送回 searchResults 组件并更改颜色 - 我无法弄清楚如何让 useEffect 向组件发送新道具。

在函数 'addFavs' 内部,我调用了原始搜索函数 'getBands' 和 'getTourBands' 来获取更新后的数据。这会添加到称为 bands、all bands 和 tourBands 的状态。我的理论是一旦这个更新的数据被添加到状态,它就会将新的道具发送到我的 searchResultsComponent。

感谢您的帮助 - 如果它太复杂,请告诉我。

    import React, { useState, useEffect } from 'react'
import { Button, Card, CardTitle } from 'reactstrap'
import LocationInput from './LocationInput'
import TypeInput from './TypeInput'
import VideosInput from './VideosInput'
import ShowsInput from './ShowsInput'
import VideoPosts from '../../SetupCards/Posts/Views/VideoPosts'
import ShowsPosts from '../../SetupCards/Posts/Views/ShowsPosts'
import FeedPosts from '../../SetupCards/Posts/Views/FeedPosts'
import { useAuth0 } from "../../../react-auth0-spa";
import BandCard from '../../BookABand/BandCard'
import SearchResults from './SearchResults'

let shouldUpdate = 0

export default function BandSearchBar(props) {
    const [ isSelected, setIsSelected ] = useState('location')
    const [ bands, setBands ] = useState([])
    const [ tourBands, setTourBands ] = useState([])
    const [ allBands, setAllBands ] = useState([])
    const [ locationText, setLocationText ] = useState('')
    const [ location, setLocation ] = useState([])
    const [ genre, setGenre ] = useState('Genre/Style')
    const [ bandTypes, setBandTypes ] = useState('all')
    const [ videosFilter, setVideosFilter ] = useState('all')
    const [ showsFiltered, setShowsFiltered ] = useState('all')
    const { getTokenSilently } = useAuth0();
    const { loading, user } = useAuth0();

    const getHomeBands = async () => {
        const token = await getTokenSilently();
        try {
            const response = await fetch(`/api/autoquotegenerators/homeBands/${location[0]}/${location[1]}`, {
                headers: {
                    Authorization: `Bearer ${token}`,
                }
            })
            const responseData = await response.json();
            if(responseData !== []){
                setBands(responseData)
            }
        } catch (error) {
            console.log(error)
        }
    }
    const getTourBands = async () => {
        const token = await getTokenSilently();
        try {
            const response = await fetch(`/api/autoquotegenerators/tourBands/${location[0]}/${location[1]}`, {
                headers: {
                    Authorization: `Bearer ${token}`,
                }
            })
            const responseData = await response.json();
            if(responseData !== []){
                setTourBands(responseData)
            }
        } catch (error) {
            console.log(error)
        }
        let allBandsArray = Array.from(bands).concat(tourBands)
            setAllBands(allBandsArray)
    }

    useEffect(() => {
        setTimeout(() => {
            if(shouldUpdate >= 1){
                getHomeBands()
                getTourBands()
            }else {
                shouldUpdate += 1
            }

        },250)
    }, [location])

    const searchLocation = (location, text) => {
        setLocation(location)
        setLocationText(text)
    }

    const showCard = (set) => {
        switch(set){
            case 'location':
                return <div><LocationInput savedLocationText={locationText} searchLocation={searchLocation} savedGenre={genre} filterByGenre={filterByGenre} /></div>
            case 'bands':
                return <div><TypeInput savedType={bandTypes} filterBandTypes={filterBandTypes} /> </div>
            case 'videos':
                return <div><VideosInput filterByVideos={filterByVideos} videosFilter={videosFilter} /> </div>
            case 'shows':
                return <div><ShowsInput filterShows={filterShows} showsFiltered={showsFiltered}/> </div>
        }
    }

    if (loading || !user) {
        return <div>Loading...</div>;
    }

    const addRockOn = async (postId, rocks, _id) => {
        const token = await getTokenSilently();
        try {
            await fetch(`/api/autoquotegenerators/posts/${_id}/${postId}`,{
                method: 'PUT',
                headers: {
                    Authorization: `Bearer ${token}`,
                    "Content-Type": "application/json; charset=UTF-8",
                },
                body: JSON.stringify({
                    rockOn: rocks
                })
            })
        } catch (error) {
            console.log(error)
        }
    }

    const addFavorites = (userId, band) => {

        if(band.favorites.includes(userId)){
            addFavs(band.favorites.filter(fav => {
                return fav !== userId
            }), band._id)

        }else {
            addFavs(band.favorites.concat(userId), band._id)
        }

    }

    const addFavs = async (favs, id) => {
        const token = await getTokenSilently();
            try{
                await fetch(`/api/autoquotegenerators/${id}`, {
                    method: 'PUT',
                    headers: {
                        Authorization: `Bearer ${token}`,
                        "Content-Type": "application/json; charset=UTF-8",
                    },
                    body: JSON.stringify({
                        favorites: favs
                    })
                })
                getHomeBands()
                getTourBands()
            } catch(error){
                console.log(error)
            }
    }

    const convertPost = (post, band) => {
        if(genre === 'Genre/Style'){
            switch (post.type) {
                case "video":
                    return (
                        <VideoPosts addFavorites={addFavorites} favorites={band.favorites} addRockOn={addRockOn} link={post.data} band={band} post={post} _id={band._id} />
                    )
                case "text":
                    return (
                        <FeedPosts addFavorites={addFavorites} favorites={band.favorites} addRockOn={addRockOn} band={band}  post={post} _id={band._id}/>
                    )   
                case "show":
                    return (
                        <ShowsPosts addFavorites={addFavorites} favorites={band.favorites} addRockOn={addRockOn} band={band} post={post} _id={band._id}/>
                    )
                default: 
                    return null;
            }
        }else {
            if(band.bandGenre === genre ){
                switch (post.type) {
                    case "video":
                        return (
                            <VideoPosts addFavorites={addFavorites} favorites={band.favorites} addRockOn={addRockOn} link={post.data} band={band} post={post} />
                        )
                    case "text":
                        return (
                            <FeedPosts addFavorites={addFavorites} favorites={band.favorites} addRockOn={addRockOn} band={band} post={post} _id={band._id} />
                        )   
                    case "show":
                        return (
                            <ShowsPosts addFavorites={addFavorites} favorites={band.favorites} addRockOn={addRockOn} band={band} post={post} _id={band._id}/>
                        )
                    default: 
                        return null;
                }
            }
        }

    }

    const convertBand = (band) => {
        if(genre === 'Genre/Style'){
            return <Button color="light" className="w-100 mb-1">
            <BandCard id={band._id} key={band.bandName} youtube={band.youtube} bandName={band.bandName} bandBio={band.bandBio} />
            </Button>
        }else {
            if(band.bandGenre === genre){
                return <Button color="light" className="w-100 mb-1">
                <BandCard id={band._id} key={band.bandName} youtube={band.youtube} bandName={band.bandName} bandBio={band.bandBio} />
                </Button> 
            }
        }
    }

    const createPromoVideo = (link, band) => {
        let post = {
            date: null
        }
        return <VideoPosts post={post} link={link} band={band} _id={band._id} />
    }

    const filterBandTypes = (type) => {
        setBandTypes(type)
    }

    const filterByGenre = (genre) => {
        setGenre(genre)
    }

    const filterByVideos = (videos) => {
        setVideosFilter(videos)
    }

    const filterShows = (shows) => {
        setShowsFiltered(shows)
    }

    return (
        <div className="d-flex flex-column">
            <div className="d-flex flex-row">
                <Button id="location" onClick={() => {setIsSelected('location')}} 
                color={isSelected === 'location' ? "dark active" : "dark"} className="w-100 h5" style={{borderTopLeftRadius: '3px', borderBottomLeftRadius: '3px', borderTopRightRadius: '0px', borderBottomRightRadius: '0px'}} >Feed</Button>
                <Button id="bands" onClick={() => {setIsSelected('bands')}} color={isSelected === 'bands' ? "dark active" : "dark"}  className="w-100 h5 rounded-0">Bands</Button>
                <Button id="videos" onClick={() => {setIsSelected('videos')}} 
                color={isSelected === 'videos' ? "dark active" : "dark"} className="w-100 h5 rounded-0">Videos</Button>
                <Button id="shows" onClick={() => {setIsSelected('shows')}}  color={isSelected === 'shows' ? "dark active" : "dark"} className="w-100 h5" style={{borderTopRightRadius: '3px', borderBottomRightRadius: '3px', borderTopLeftRadius: '0px', borderBottomLeftRadius: '0px'}}>Shows</Button>
            </div>
            <div>
                {isSelected ? showCard(isSelected) : null}
            </div>
            <SearchResults isSelected={isSelected} bandTypes={bandTypes} allBands={allBands} bands={bands} tourBands={tourBands} convertPost={convertPost} convertBand={convertBand} videosFilter={videosFilter} showsFiltered={showsFiltered} createPromoVideo={createPromoVideo}/>
        </div>
    )
}

我也尝试过使用 useEffect() 挂钩调用一个函数来显示带有新道具的组件。依然没有。 ** 当我尝试使用效果时,我让它听'bands'、'allBands'和'tourBands'。如果他们改变了,它会将组件传递给一个函数,该函数将在渲染中显示它——这没有用,所以我没有将它包含在我上面的代码中。

这里是file/componentSearchRestuls.js

        import React from 'react'

    export default function SearchResults(props) {
    const {isSelected, bandTypes, allBands, bands, tourBands, convertPost, convertBand, videosFilter, showsFiltered, createPromoVideo} = props

    return (
    <div>
    <div style={{
        display: isSelected === 'location' ? 'block' : 'none'
    }}>

        {bandTypes === 'all' ? allBands.map(band => {
            return band.posts.map(post => {
                let currPost = convertPost(post, band)
                return currPost
            })
        }).reverse() : null}

        {bandTypes === 'local' ? bands.map(band => {
            return band.posts.map(post => {
                let currPost = convertPost(post, band)
                return currPost
            })
        }).reverse() : null}

        {bandTypes === 'touring' ? tourBands.map(band => {
            return band.posts.map(post => {
                let currPost = convertPost(post, band)
                return currPost
            })
        }).reverse() : null}

    </div>
    <div style={{
        display: isSelected === 'bands' ? 'block' : 'none'
    }}>
        {bandTypes === 'all' ? allBands.map(band => {
            let currBand = convertBand(band)
            return currBand
        }) : null}
        {bandTypes === 'local' ? bands.map(band => {
            let currBand = convertBand(band)
            return currBand
        }) : null}
        {bandTypes === 'touring' ? tourBands.map(band => {
            let currBand = convertBand(band)
            return currBand
        }) : null}
    </div>
    <div style={{
        display: isSelected === 'videos' ? 'block' : 'none'
    }}>
        {bandTypes === 'all' && videosFilter === 'all' ? allBands.map((band) => {
            return band.posts.map(post => {
                if(post.type === 'video'){
                    let currBand = convertPost(post, band)
                    return currBand
                }
            })
        }) : null}
        {bandTypes === 'local' && videosFilter === 'all' ? bands.map((band) => {
            return band.posts.map(post => {
                if(post.type === 'video'){
                    let currBand = convertPost(post, band)
                    return currBand
                }
            })
        }) : null}
        {bandTypes === 'touring' && videosFilter === 'all' ? tourBands.map((band) => {
            return band.posts.map(post => {
                if(post.type === 'video'){
                    let currBand = convertPost(post, band)
                    return currBand
                }
            })
        }) : null}

        {bandTypes === 'all' && videosFilter === 'promo' ? allBands.map((band) => {
            return band.youtube.map(link => {
                let currVid = createPromoVideo(link, band)
                return currVid
            })
        }) : null}
    </div>
    <div style={{
        display: isSelected === 'shows' ? 'block' : 'none'
    }}>
        {bandTypes === 'all' && showsFiltered === 'all' ? allBands.map((band) => {
            return band.posts.map(post => {
                if(post.type === 'show'){
                    let currBand = convertPost(post, band)
                    return currBand
                }
            })
        }) : null}
        {bandTypes === 'local' && showsFiltered === 'all' ? bands.map((band) => {
            return band.posts.map(post => {
                if(post.type === 'show'){
                    let currBand = convertPost(post, band)
                    return currBand
                }
            })
        }) : null}
        {bandTypes === 'touring' && showsFiltered === 'all' ? tourBands.map((band) => {
            return band.posts.map(post => {
                if(post.type === 'show'){
                    let currBand = convertPost(post, band)
                    return currBand
                }
            })
        }) : null}

        {bandTypes === 'all' && showsFiltered === 'upcoming' ? allBands.map((band) => {
            return band.posts.map(post => {
                if(post.type === 'show'){
                    let performanceDateUnformatted;
                    performanceDateUnformatted = post.details.filter(detail => {
                        if(detail.title === 'Performance Date'){
                            return detail.detail
                        }
                    })[0].detail

                    var months = {
                        'Jan' : '01',
                        'Feb' : '02',
                        'Mar' : '03',
                        'Apr' : '04',
                        'May' : '05',
                        'Jun' : '06',
                        'Jul' : '07',
                        'Aug' : '08',
                        'Sep' : '09',
                        'Oct' : '10',
                        'Nov' : '11',
                        'Dec' : '12'
                    }

                    let year = performanceDateUnformatted.slice(11)
                    let day = performanceDateUnformatted.slice(8,10)
                    let month = months[performanceDateUnformatted.slice(4,7)]

                    let showDateFormatted = new Date(year, month - 1, day)

                    let today = new Date()

                    let todayPlusOneWeek = new Date(today.getUTCFullYear(), today.getUTCMonth(), today.getDate() + 7)

                    if(showDateFormatted > today && showDateFormatted < todayPlusOneWeek ){
                        let currBand = convertPost(post, band)
                        return currBand
                    }else {
                        return null
                    }
                }
            })
        }) : null}
    </div>
    </div>
    )
    }

我不太熟悉 async/await 的反应,但也许它有问题?您的代码似乎是正确的,但是,例如,这个条件有时真的有效吗?

const responseData = await response.json();
if (responseData !== []) {
  setTourBands(responseData);
}

您的代码库非常复杂,因此很难说清楚到底发生了什么,但据我所知,您的状态有点不同步,因此值的更新时间比您预期的要晚。

我认为问题是由于 getHomeBandsgetTourBands 是独立的函数,而 getTourBands 中的 setAllBands 依赖于 bandsgetHomeBands。当您非常快速地调用多个更改时,React 不保证状态会是最新的,在这里它们可能几乎同时发生。此外,您正在进行网络提取,这可能会以与调用它们不同的顺序完成,这意味着新的 bands 可能还没有到达。

要解决此问题,只需合并 2 个函数并使用独立于当前状态的数据调用所有 3 个 setStates。

    const getBands = async () => {
        const token = await getTokenSilently();
        try {
            let response = await fetch(`/api/autoquotegenerators/homeBands/${location[0]}/${location[1]}`, {
                headers: {
                    Authorization: `Bearer ${token}`,
                }
            })
            const newBands = await response.json();
            if(newBands !== []){
                setBands(newBands)
            }

            response = await fetch(`/api/autoquotegenerators/tourBands/${location[0]}/${location[1]}`, {
                headers: {
                    Authorization: `Bearer ${token}`,
                }
            })
            const newTourBands = await response.json();
            if(newTourBands !== []){
                setTourBands(newTourBands)
            }

            if((newBands !== []) && (newTourBands !== [])){
                setAllBands([ ...newBands, ...tourBands])
            }
        } catch (error) {
            console.log(error)
        }
    }