【问题标题】:Send component new props react - Stuck发送组件新道具反应 - 卡住
【发布时间】:2020-08-12 19:35:29
【问题描述】:

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

这就是发生的事情。

首先,用户单击帖子组件上的按钮...它会触发下面父组件上的“添加收藏夹”和“添加收藏夹”功能。这两个函数确定是否应该将用户添加到收藏夹或从收藏夹中删除用户。

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

在函数“addFavs”内部,我调用原始搜索函数“getBands”和“getTourBands”来获取更新的数据。这被添加到称为bands、allbands 和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'。如果他们进行了更改,它会将组件传递给将在渲染中显示它的函数 - 这不起作用,所以我没有将它包含在上面的代码中。

这里是文件/组件 SearchRestuls.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>
    )
    }

【问题讨论】:

  • convertPost 的参数是否基于SearchResults 组件内的任何内容?传递生成帖子的整个函数听起来可能是导致此问题的原因。 (如果您发布 SearchResults 代码将是理想的)
  • 我刚刚添加了上面的 SearchResults 代码 - SearchResults 最初都在 BandSearchBar 的渲染中。我认为它可能会起作用,出于某种原因,如果它被移动到它自己的组件中,但唉,我在这里哈哈
  • 刚发现——第一次点击更新数据库,但是没有通过props。第二次点击更新道具但不更新数据库。
  • 在分离组件之前它工作正常吗?
  • 在我分离之前它的工作方式相同。

标签: reactjs react-props use-effect use-state


【解决方案1】:

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

我相信这个问题是由 getHomeBandsgetTourBands 是独立的函数和 getTourBands 中的 setAllBands 依赖于 bands 引起的,这些 getHomeBands 中已更改。 React 不保证当您非常快速地调用多个更改时状态将是最新的,并且它们可能几乎同时发生。此外,您正在执行网络提取,这可能会以不同于您调用它们的顺序完成,这意味着新的 bands 可能还没有到达。

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

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

【讨论】:

  • 今天你教我异步函数!有用!我真的认为这是更新道具的问题 - 啊!再次感谢您的帮助!
  • 没问题!我很高兴你学到了一些东西。状态的异步特性很容易出错。而且,一旦您向其中添加其他异步内容(例如网络),顺序可能会完全混乱,从而导致像您在这里一样的奇怪行为。
  • 嘿伙计!你知道如何在反应中预加载背景图像吗?它和 HTML 中的一样吗? stackoverflow.com/questions/61576115/…
  • 你好,亚当!你知道正则表达式吗? stackoverflow.com/questions/61732102/…
【解决方案2】:

我对 react 中的 async/await 不是很熟悉,但它可能有问题吗?您的代码似乎是正确的,但是,例如,这种情况有时真的有效吗?

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

【讨论】:

  • 嘿!感谢您的回复 - 我应该更具体。我尝试使用 useEffect 监听我存储数据的状态 - band、allBands 和 tourBand。
  • 是的,对不起,我再次阅读了您的问题并编辑了我的答案哈哈
  • 嗯 - 我刚刚删除了它来尝试它,它不会影响结果。我在那里的原因是,当用户在他们的位置键入时,地理编码器将城市转换为坐标数组以发送到“getHomeBands”和“getTourBands” - 有时它会发回一个空数组,导致错误SearchResults 组件将尝试映射空结果。无论哪种方式 - 现在有或没有 if 语句,它都会做同样的事情。
  • 地理编码器?是什么哈哈,我在说 getHomeBands() 方法,您可以在其中获取数据,然后将响应转换为 json,但是您正在异步使用它,所以我想知道 .json() 方法是否不太长,所以条件下面导致错误,因此跳过 setBands() 调用
猜你喜欢
  • 1970-01-01
  • 2017-07-10
  • 1970-01-01
  • 2021-02-22
  • 2020-02-14
  • 1970-01-01
  • 2020-09-01
  • 2019-01-08
  • 2020-12-25
相关资源
最近更新 更多