Execute a function after iterating completely over all elements - reactjs

I have a json object messages that I want to display and iterate over using the map function. This is loaded at the very beginning. There are 23 elements in it, but it takes a short moment until all elements are displayed. I would like to write a loading function, i.e. as soon as all messages have been iterated over, a function should be called.
I have tried this here, but this function is not executed.
Now my question, how can I execute the function loading, as soon as over all elements was iterated over?
Note: I have update the message Json and this send also works in real time, but only the initial loading takes too long and therefore I would like to show the user something. A loading bar should be displayed and as soon as the complete list has been iterated over, the list should be removed or no longer displayed and the user should receive the complete loaded list.
function MessageSide(props) {
const [loaded, setLoaded] = useState(false); // added
const loading = () => {
console.log("LOADED")
setLoaded(true) // added
}
return (
<div class="col flex-fill background--chat">
<div className="content__body">
// added
{loaded? false :
<div>
<Ellipsis color="#5869FF" style={{left:"50", right:"50", top:"50", bottom:"50",}}/>
</div>
} // added
<div className="item" style={loaded? {} : {display: 'none'}}>
{messages.map((message) => {
// onLoad={() =>test()} that didn't work
return (
<div>
<Message
message={message.body}
id={message.id}
timestamp={message.timestamp}
chatid={message.chatid}
onLoad={() =>loading()} // this is what I tried
/>
</div>
);
})}
</div>
<div ref={messagesEndRef}/>
</div>
</div>
);
}
export default MessageSide;

The loading is rather not to be determined in return, let alone inside a loop.
You should include code of <Message> to your question.
const [initialLoad, setInitialLoad] = useState(false)
useEffect(()=>{
if(message.length > 0) { setInitialLoad(true) }
},[message])
useEffect(()=>{ if(message.length > 0) {
/*stop showing loading screen */
} },[initialLoad])`
This way, variable initialLoad will be false at first render, but will be changed into true after load. However it will not change afterwards, causing the last useEffect hook to be executed only once

Related

.filter() function creating loop in delete function - React

I've got a 'list' component, which allows you to add an item to a state array, and then display it from the state afterwards. These list items can then be removed by the user afterwards (or should be able to).
There's four state props in this component:
currentList: content in the input that's to be added to the list array
setCurrentList: what's used to change the content in the input
fullList: the full array list
setFullList: used to add the currentList content to the array, and removed
I'm using .filter() to create a copy of the state array, and then set the state afterwards in this function:
const deleteFromList = (e) => {
console.log("Delete button pressed")
console.log(e)
let fullList = props.fullListState
let setFullList = props.setFullListState
let filteredArray = fullList.filter(item => item)
setFullList(filteredArray)
}
However, every time I execute this function (i.e. when the delete button is pressed), it just creates a loop and the first two console.logs are just repeatedly done.
This is the full return function itself:
<>
<label className="setup-jobs-label">{props.label}</label>
<div className="setup-jobs-input-container">
<input className="setup-jobs-alt-input" type="text" onChange={onChange} value={props.currentListState} />
<button className="setup-jobs-add-button" onClick={addToList}>Add</button>
</div>
{ props.fullListState === [] ? null : props.fullListState.map(x => {
return <div className="setup-jobs-input-container" key={props.fullListState[x]}>
<p className="setup-jobs-input-paragraph">{x}</p>
<button className="setup-jobs-delete-button" onClick={deleteFromList(x)}>Delete</button>
</div>
}) }
</>
The important bit is the bottom conditional render, which checks to see if the state array is empty, and if so, not display anything. If it isn't, then it returns null.
Any advice would be appreciated - not sure what I'm doing wrong in the filter function.
In your onClick handler, you pass the result of the execution of deleteFromList, you should pass a reference to this function instead :
// note the '() =>'
<button className="setup-jobs-delete-button" onClick={() => deleteFromList(x)}>Delete</button>
See https://reactjs.org/docs/handling-events.html for more details about this.
Beside this, your filter logic does not seem right :
// this line only removes falsy values, but not the "e" values
let filteredArray = fullList.filter(item => item)
// you should implement something like this
let filteredArray = fullList.filter(item => [item is not "e"])
// this should work as we work on objects references
let filteredArray = fullList.filter(item => item !== e)

The useEffect won't change the state LIVE when clicked but content does change on reload

I'm trying to change my page's content when clicking on the link but the thing is it only changes on reload and not directly when clicked.
function Home( { category } ) {
const [news, setNews] = useState([]);
useEffect(() => {
async function newsArticles(){
const request = await axios.get(category);
console.log(request.data.articles);
setNews(request.data.articles);
return request;
}
newsArticles();
}, [category]);
return (
<div className='home'>
{news.map(ns => (
<NewsCard className='homeNewsCard' key = {ns.source.id} title = {ns.title} description = {ns.description} image = {ns.urlToImage} link = {ns.url}/>
))}
</div>
)
}
Since the effect captures the values at the time the component is rendered, any asynchronous code that executed in the effect will also see these values rather than the latest values. Example :
useEffect(()=>{
asyncFunc(props.val)
.then((res)=> setData(res + props.oth))
},[props.val]);
props.oth is used when the promise resolves, but the effecct captures the values of props.val and prips.oth at the time of render , the value of props.oth had changed during the time it took for the promise to reoslve , the code would see the old value
Solution :
you can either use reference to store the value of props.oth so your code will always see the latest value or you can add props.oth back to dependency array and return a cleanup function .
Example :
useEffect(()=>{
let cncl = false
asyncFunc(props.val)
.then((res)=>{
if(!cncl)
setData(res + props.oth)})
return ()=> cncl = true}
,[props.val, props.oth])
cncl flag is raised before running the effecct again (when props.val or props.oth have changes comapred with the previous render).

Ternary operator in Typescript based Reactjs does not work as I expected

I am newbie in TypeScript+ReactJS
In my code Ternary operator does not work as I expect.
This is my code:
import React, { SyntheticEvent,useRef,useState } from "react";
import Result from './Result';
//main application
const App:React.FC=()=>{
const [searchResult,setSearchResultState]=useState<Array<Object>>([]);
const setSearchResultFunc = (value:Array<Object>)=>{
setSearchResultState(value);
}
return (
<section id="App">
<SearchPanel setResult={setSearchResultFunc}/>
{
(searchResult===[])
?""
:<Result result={searchResult}/>}
<footer className={searchResult===[]?"absolute":""}>
<p>All data are received using Google Place API. {"result: " +searchResult}
<br/>
This website is not actual working website and is made just for educational purpose.
</p>
</footer>
</section>
)
}
export default App;
interface searchProps{
setResult:(value: Array<Object>) => void
}
const SearchPanel=({setResult}:searchProps)=>{
const searchElementRef = useRef(null as any);
const search =(e:SyntheticEvent)=>{
const searchValue = searchElementRef.current.value;
if(typeof searchValue!=="string"){
alert("only text allowed");
}else{
//validate user search input
regexValidate(searchValue)
?alert("No special characters are allowed")
//send request to server if no special character is found
:fetch("//localhost:3000/city?name="+searchValue,{
method:'GET',
credentials:'include',
headers:{
'Accept':'application/json',
'Content-Type':'application/json'
}
}).then((data:any)=>{
return data.json();
}).then(data=>{
setResult(Object.entries(data));
})
}
e.preventDefault();
}
const regexValidate = (value:string)=>{
const specialChars = /[`!##$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/;
return specialChars.test(value);
}
return(
<section className="text-center absoluteCenter">
<h1 className="text-white text-lg uppercase mb-5">Wanna travel somewhere?</h1>
<form action="/" method="GET" onSubmit={e=>search(e)}>
<input type="text" placeholder="SEARCH FOR CITY ex:London" ref={searchElementRef}/>
</form>
</section>
)
}
So, what I expect is, when application starts, the App component will have an empty Array state which is called searchResultFunc. And when user performs a search, it will update the state by calling function setResultState. The function will have data that search has returned and will alter previous state data to the new data.
According to this ternary operator,
{
(searchResult===[])
?""
:<Result result={searchResult}/>
}
I thought the page should not have Result component from beginning, but when I render the page for first time,
I already see Result in HTML inspector which means ternary operator does not work as I expected.
To check the value of state when the page renders for first time, I made such code
<p>All data are received using Google Place API. {"result: " +searchResult}
<br/>
This website is not actual working website and is made just for educational purpose.
</p>
If state searchResult has some data, the webpage should show value of the state. However, the result state is empty.
What did I do wrong?
you need to change searchResult===[] to searchResult.length === 0
When you compare arrays using === you are actually checking whether the two arrays are the same array. It does not check whether the two arrays have the same content.
Here's a short program that illustrates the difference: https://www.typescriptlang.org/play?#code/MYewdgzgLgBAhgLhtATgSzAcwNoF0YC8MeAUKJLAEZKoY75GnnQzA1TpZ6Hwl-MgANgFMAdIJCYAFAHI4BApQD8MgDTwFlAJRlwEIWInS5C4CvXyCwHXyA
You need to check whether array is empty or not. You can do the following.
arr=[]
arr.length === 0? True : False
arr===[] does not work as it checks the array reference whether both the array points toward same memory reference. Hence it will fail everytime even if elements are same in both array unless and until it's same array referenced.

Why does #use-gesture stop sending state updates when mouse is outside of containing div?

I'm using #use-gesture/react to drag an element around. I have an element on a page with the useDrag bind hook attached:
<Grid item xs={3}>
<div {...eventHandlers}> // <- memo-ized hook
<ColumnDragCard
isShadow={!(!dragState && !(dragState ? false : false))}
isDraggable={!dragState && !(dragState ? false : false)}
log={true}
/>
</div>
<ColumnDragObject dragState={dragState} />
</Grid>
When the useDrag start event fires on the <div>, dragState is set, and the <ColumnDragObject creates a portal with another <ColumnDragCard>:
const dragObjectPortal =
dragState && dragState.pointerStartInfo
? createPortal(
<div style={dragObjectStyle}>
<div style={dragObjectPositionerStyle} ref={dragBoxRef}>
<div style={dragObjectHolderStyle}>
<ColumnDragCard isDragged />
</div>
</div>
</div>,
document.body
)
: null;
I then update the position of the copied card, based on the xy reported from the hook:
useLayoutEffect(() => {
if (dragState) {
dragState.updateListeners["dragObj"] = (xy, elapsedTime) => {
if (!dragBoxRef.current) {
return;
}
console.log(`Mouse is at x:${xy[0]}, y:${xy[0]}`);
console.log(`Time since start: ${elapsedTime}`);
dragBoxRef.current.style.left = `${xy[0]}px`;
dragBoxRef.current.style.top = `${xy[1]}px`;
};
}
}, [dragState]);
The problem I'm having is, when the pointer is moved outside of what appears to be the original <div>, useDrag stops reporting state changes. I have it in a codesandbox. You can see that when you move the mouse outside of a certain range, the console.logs stops reporting. But, if you move it back inside and release, the log of elapsedTime indicates the hook was listening the whole time ... it just stopped reporting back.
Any ideas on how I fix this?

React function in displaying returned value not working

I am trying to display calculated values from a function called renderAnalysis - all which works until the bottom of the function where I am trying to return a div with the values that are found. However, this isn't displaying (all values are returned though in the console when I test it out) and was wondering what I need to adjust to prevent the values I can show in the console? I think I'm doing a render call wrong or something... I've added the code for reference. Thank you!
renderAnalysis = () => {
let reactions = this.state.reactions;
let foods = Object.keys(reactions);
foods.map((food) => {
let reactionDescriptions = reactions[food];
let reactionDescriptionKeys = Object.keys(reactionDescriptions);
let responseKey = Object.keys(reactionDescriptions);
responseKey.map((singleDescription) => {
let value = reactionDescriptions[singleDescription];
// RETURN VALUE NOT SHOWING
return <div> {(food, singleDescription, value)} </div>;
});
});
};
Further information: There is a toggle I made in state that if someone clicks a button it triggers this function. Basically
<button onClick={this.displayRenderAnalysisToggle}>
Render Analysis
</button>{" "}
<br />
{this.state.displayRenderAnalysisToggle ? this.renderAnalysis() : null}
</div>

Resources