问题
I have been stuck for a few hours now.. I am trying to send a component new props... but It wont get the new ones.
Here is what happens.
First the user clicks a button on the post component... It fires the function, 'add favorites' and 'addFavs' on the parent component below. These two functions determine if it should add the user to favorites or delete the user from favorites.
When the user is added as a favorite, the button they clicked, inside the searchResults component, appears a different color. The problem is they have to click the button twice for the new props to be sent back to the searchResults component and change the color - and I cant figure out how to get useEffect to send the component the new props.
Inside of the function 'addFavs', I call the origial search functions, 'getBands' and 'getTourBands' to fetch the updated data. This gets added to the states called bands, all bands, and tourBands. My theory was once this updated data gets added to the state, it would send the new props to my searchResultsComponent.
Thanks for your help - let me know if its too complicated.
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>
)
}
I have also tried to use the useEffect() hook to call a function to display the component with new props. Still nothing. ** When I tried the use effect, I had it listen to 'bands', 'allBands', and 'tourBands'. If they changed it would pass the component to a function that would display it in the render - This didnt work so I didnt include it in my code above.
Here is the file/component 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>
)
}
回答1:
Your code base is super complex so it's hard to tell what exactly is going on, but as far as I can tell, your state is getting somewhat desynced, thus the values update later than you expect them.
I believe the issue is caused by the getHomeBands
and getTourBands
being separate functions and setAllBands
in getTourBands
relying on bands
that are changed in getHomeBands
. React doesn't promise the state will be up to date when you call multiple changes very quickly and here they might happen almost simultaneously. Furthermore you are doing network fetches, which might finish in a different order than you called them in, meaning that the new bands
might not have even arrived yet.
To fix this, just merge the 2 functions and call all 3 setStates with data that is independent of the current state.
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)
}
}
回答2:
I'm not really familiar with async/await in react, but maybe there a problem with it ? Your code seems to be correct, but, for exemple, is this conditions really valid sometimes ?
const responseData = await response.json();
if (responseData !== []) {
setTourBands(responseData);
}
来源:https://stackoverflow.com/questions/61489514/send-component-new-props-react-stuck