I have some datas, who I can get my data when I'm in the loop. But, if I set the data in a useState array value, I just have the last value.
How to get and show all the data in the render ?
My code out :
const [messagesList, setMessagesList] = useState([])
My code at useEffect :
firebase.database().ref(`accounts/${username}/messages/`).on('value', (snapshot) => {
const jsonData = snapshot.toJSON()
const keys = Object.keys(jsonData)
const finalData = keys.map(key => {
const element = jsonData[key]
for (const i in element) {
const elementFinal = element[i]
setMessagesList([elementFinal])
}
})
})
My code at render :
{messagesList.map((x, i) => (
<div key={i}>
<p key={i}>{x.senderUsername} said "{x.message}"</p>
</div>
))}
My Firebase database :
My result :
You are erasing the value of messageList. Let say, elementFinal equals '45'
setMessagesList([elementFinal])
will result in
messagesList // ['45']
You need to read the previous value (the shallow copy is important) and set the new one:
const array = [...messagesList];
array.push(elementFinal);
setMessagesList(array)
EDIT: I didnt see that it could be reduced as follow:
const messages = [];
const finalData = keys.map(key => {
const element = jsonData[key]
for (const i in element) {
messages.push(element[i]);
}
})
setMessagesList(messages);
Related
I'm taking an array and filtering the value in a context:
const { responsible } = useResponsible()
const [ids, setIds] = useState([])
const filteredResponsible = responsible?.filter((resp) =>
ids.includes(resp.id)
)
The problem is that I need to make a map to get the corresponding value of each id, one by one. This ends up making the code too long:
const { filteredResponsible } = useResponsible
const responsibleName = filteredResponsible.map((resp) => resp.name)
const responsibleEmail = filteredResponsible.map((resp) => resp.email)
const responsibleAddress = filteredResponsible.map((resp) => resp.address)
...
And so on with each item in the array.
I'm using the React Hook Form's setValue to set the value in the inputs:
useEffect(() => {
setValue('name', `${responsibleName}`)
setValue('email', `${responsibleEmail}`)
setValue('address', `${responsibleAddress}`)
setValue('cep', `${responsibleCep}`)
setValue('district', `${responsibleDistrict}`)
setValue('city', `${responsibleCity}`)
setValue('state', `${responsibleState}`)
setValue('phone', `${responsiblePhone}`)
setValue('sex', `${responsibleSex}`)
}, [])
How can I make these maps smaller? Without having to make a map to get each item in the array?
There doesn't seem to be any reason to do those map calls on every render and to do them anywhere other than where you need them, since you only show using the result in a mount-only effect. Just do them there:
const { filteredResponsible } = useResponsible; // Is there really no `()` needed here?
useEffect(() => {
setValue("name", `${filteredResponsible.map(({name}) => name)}`);
setValue("email", `${filteredResponsible.map(({email}) => email)}`);
setValue("address", `${filteredResponsible.map(({address}) => address)}`);
// ...
}, []);
If you really need those distinct arrays on every render, unless you can change your data structures to be more amenable to your output I don't see you have a lot of options. You can at least avoid multiple loops through filteredResponsible:
const { filteredResponsible } = useResponsible; // ()?
const responsibleName = [];
const responsibleEmail = [];
const responsibleAddress = [];
for (const { name, email, address } of filteredResponsible) {
responsibleName.push(name);
responsibleEmail.push(email);
responsibleAddress.push(address);
}
And if that's really the case, you may want to avoid doing it on every render:
const { filteredResponsible } = useResponsible; // ()?
const { responsibleName, responsibleEmail, responsibleAddress } = useMemo(() => {
const responsibleName = [];
const responsibleEmail = [];
const responsibleAddress = [];
for (const { name, email, address } of filteredResponsible) {
responsibleName.push(name);
responsibleEmail.push(email);
responsibleAddress.push(address);
}
return { responsibleName, responsibleEmail, responsibleAddress };
}, [filteredResponsible]);
in my application, there are two types of filters, category and country. However, I am not able to get them to be applied at the same time. For example, I only want the intersection of Category: SaaS + Country: Singapore.
Any advice?
const loadData = props.load
const [card, setCard] = useState(loadData)
const [searchPhrase, setSearchPhrase] = useState("")
const search = (event)=>{
const matchedUsers = loadData.filter((card)=>{
return card.title.toLowerCase().includes(event.target.value.toLowerCase())
})
setCard(matchedUsers)
setSearchPhrase(event.target.value)
}
const filterCountry = (event)=>{
const filteredCards = loadData.filter((card)=>{
return card.country.includes(event.target.value)
})
setCard(filteredCards)
}
const filterCat = (event)=>{
const filteredCards = loadData.filter((card)=>{
return card.cat.includes(event.target.value)
})
setCard(filteredCards)
}
You can change your filter condition to check if the value is in all your considered types
const result = yourData.filter(item => item.country.includes(YOURPHRASE) || item.cat.includes(YOURPHRASE))
you can pass the filtered array as a parameter to the filtering functions :
const search = (event)=>{
const matchedUsers = loadData.filter((card)=>{
return card.title.toLowerCase().includes(event.target.value.toLowerCase())
})
setSearchPhrase(event.target.value);
return matchedUsers
}
const filterCountry = (event,array)=>{
return array.filter((card) => card.country.includes(event.target.value);
}
const filterCat = (event,array)=>{
return array.filter((card) => card.cat.includes(event.target.value);
}
useEffect(() => {
let result = matchedUsers();
result = filterCountry(result);
result = filterCat(result);
setArrayToFilter(result);
}, [searchPhrase]);
These are parts of my entire code. So what I am trying to do is create separate arrays of the values I like or dislike and output them in my html File onclick. I tried to create an empty array and push value but my final array ends up empty.
Script.js
const showRandomMovie = async() => {
const movieInfo = document.getElementById('movieInfo');
if (movieInfo.childNodes.length > 0) {
clearCurrentMovie();
};
const movies = await getMovies();
const randomMovie = getRandomMovie(movies);
const info = await getMovieInfo(randomMovie);
displayMovie(info);
};
playBtn.onclick = showRandomMovie;
helper.js
const displayMovie = (movieInfo) => {
const moviePosterDiv = document.getElementById('moviePoster');
const movieTextDiv = document.getElementById('movieText');
const likeBtn = document.getElementById('likeBtn');
const dislikeBtn = document.getElementById('dislikeBtn');
// Create HTML content containing movie info
const moviePoster = createMoviePoster(movieInfo.poster_path);
const titleHeader = createMovieTitle(movieInfo.title);
const overviewText = createMovieOverview(movieInfo.overview);
const releaseHeader = createReleaseDate(movieInfo.release_date)
// Append title, poster, and overview to page
moviePosterDiv.appendChild(moviePoster);
movieTextDiv.appendChild(titleHeader);
movieTextDiv.appendChild(overviewText);
movieTextDiv.appendChild(releaseHeader)
showBtns();
likeBtn.onclick = likeMovie;
dislikeBtn.onclick = dislikeMovie;
};
const likeMovie = () => {
clearCurrentMovie();
showRandomMovie();
};
// After disliking a movie, clears the current movie from the screen and gets another random movie
const dislikeMovie = () => {
clearCurrentMovie();
showRandomMovie();
};
Create arrays for the likes and dislikes and push it to an array. Pass it along to your methods.
likeBtn.onclick = () => rateMovie('likes', movieInfo);
dislikeBtn.onclick = () => rateMovie('dislikes', movieInfo);
have the method add it to the array
const ratings = {
likes: [],
dislikes: [],
};
const rateMovie = (type, data) => {
ratings[type].push(data);
clearCurrentMovie();
showRandomMovie();
};
I'm using useEffect to fetch some data from Trello and set some states. First I grab the card I'm looking for and call setCard and setCardLocation. Everything is working fine. Then I get into my else case and no matter what I do setPublishDate will never be set, the loop continues to run. Why do all of these other hooks work but my last one doesn't? Thanks.
export default function Home(props) {
const [performedFetch, setPerformedFetch] = useState(false);
const [slug, setSlug] = useState(null);
const [cardLocation, setCardLocation] = useState(1);
const [card, setCard] = useState(null);
const [publishDate, setPublishDate] = useState(null);
const key = ''; // imagine these are here
const token = '';
useEffect(() => {
setSlug(
new URLSearchParams(window.location.search).get('slug')
);
if (!performedFetch && !!slug) {
fetch(`https://api.trello.com/1/lists/${listId}/cards?key=${key}&token=${token}`)
.then(response => response.json())
.then(data => {
setPerformedFetch(true);
data.forEach((c, index) => {
if (c.desc.includes(slug)) {
setCard(c)
setCardLocation(index + 1)
} else if (!publishDate && index > cardLocation) {
console.log(publishDate); // why is this always null?? also runs multiple times
const name = c.name;
const frontHalf = name.split("/")[0].split(" ");
const month = frontHalf[frontHalf.length - 1];
const day = name.split("/")[1].split(")")[0];
setPublishDate(`${month}/${day}`);
}
});
});
}
});
As already mentioned by #TaghiKhavari, you should have two useEffects (Multiple effects to separate concerns).
Also, it is important to optimize the performance by skipping effects by providing a dependency array as second argument to the useEffect. So the effect will only re-run if any of its dependencies would change.
First effect for slug:
useEffect(() => {
setSlug(
new URLSearchParams(window.location.search).get('slug')
);
}, []) // Note: Remove "[]" if you want to set slug at each update / render Or keep it if you want to set it only once (at mount)
Second effect to fetch and set card and other details:
useEffect(() => {
if (!performedFetch && slug) {
fetch(
`https://api.trello.com/1/lists/${listId}/cards?key=${key}&token=${token}`
)
.then((response) => response.json())
.then((data) => {
setPerformedFetch(true)
// Note: if there can be only ONE matching card
const index = data.findIndex((card) => card.desc.includes(slug))
if (index > -1) {
const card = data[index]
setCard(card)
setCardLocation(index + 1)
const name = card.name
const frontHalf = name.split('/')[0].split(' ')
const month = frontHalf[frontHalf.length - 1]
const day = name.split('/')[1].split(')')[0]
setPublishDate(`${month}/${day}`)
}
// Setting State in a LOOP? is a problem
/*
data.forEach((card, index) => {
if (card.desc.includes(slug)) {
setCard(card)
setCardLocation(index + 1)
} else if (!publishDate && index > cardLocation) {
const name = card.name
const frontHalf = name.split('/')[0].split(' ')
const month = frontHalf[frontHalf.length - 1]
const day = name.split('/')[1].split(')')[0]
setPublishDate(`${month}/${day}`)
}
})*/
})
}
}, [slug, performedFetch])
Set states may be async to improve performance:
So, you should not set states in a loop as you are doing currently. If you must iterate through a loop and set all or few elements of the array in state, you can loop through the array and push all relevant items in a local array variable and set it to state after loop ends. Hope it helps!
It's because usually react states updates asynchronously and at the time you're checking for slug it hasn't set yet
you need to do something like this:
function Home(props) {
const [performedFetch, setPerformedFetch] = useState(false);
const [slug, setSlug] = useState(null);
const [cardLocation, setCardLocation] = useState(1);
const [card, setCard] = useState(null);
const [publishDate, setPublishDate] = useState(null);
const key = ""; // imagine these are here
const token = "";
useEffect(() => {
setSlug(new URLSearchParams(window.location.search).get("slug"));
});
useEffect(() => {
console.log(slug)
if (!performedFetch && !!slug) {
fetch(`https://api.trello.com/1/lists/${listId}/cards?key=${key}&token=${token}`)
.then(response => response.json())
.then(data => {
setPerformedFetch(true);
data.forEach((c, index) => {
if (c.desc.includes(slug)) {
setCard(c)
setCardLocation(index + 1)
} else if (!publishDate && index > cardLocation) {
console.log(publishDate); // why is this always null?? also runs multiple times
const name = c.name;
const frontHalf = name.split("/")[0].split(" ");
const month = frontHalf[frontHalf.length - 1];
const day = name.split("/")[1].split(")")[0];
setPublishDate(`${month}/${day}`);
}
});
});
}
}, [slug, performedFetch])
}
I'm following a react tutorial but I'm lost. I don't understand starting line 9.
so I tried to make a little miarature
const updateTodo = (list, updated) => {
const index = list.findIndex(item => item.id === updated.id)
return [
...list.slice(0,index),
updated,
...list.slice(index+1)
]
}
https://jsbin.com/sifihocija/2/edit?js,console but failed to produce the result that the author did, what's wrong?
Issue is in this line:
const index = list.findIndex(item => item.id === updated.id)
updated is an array, to access the id, you need to specify the index also,
for other array you are using loop, so item will be the each object of the array, and item.id will give you the id of each object, try this:
const index = list.findIndex(item => item.id === updated[0].id)
const arr = [
{id:1,name:'hello'},
{id:2,name:'hai'}
]
const arr2 = [
{id:2,name:'this should be a new string'}
]
const updateTodo = (list, updated) => {
const index = list.findIndex(item => item.id === updated[0].id);
return [
...list.slice(0,index),
...updated,
...list.slice(index+1)
]
}
console.log(JSON.stringify(updateTodo(arr,arr2)))
Check the working code: https://jsbin.com/pazakujava/edit?js,console
Let me know if you need any help in this.
It's simpler and cleaner using Array.map:
const updateTodo = (list, updated) =>
list.map((item) => {
if (item.id !== updated.id) {
return item;
}
return updated;
});