React is taking way more time for execution than expected - javascript

I have tried to implement drench game. I have attached my code below. The logic is correct and it shouldn't also take so much time for execution given it is a 14x14 board. But after some clicks when the state.covered covers about quarter of the board the execution is taking way long to complete.
import './App.css';
import { useState } from 'react'
function App() {
const colors = ['aliceblue', 'tomato', 'darksalmon', 'thistle', 'olivedrab', 'steelblue']
var dp = [];
for(let i=0;i<14;i++){
let temp = []
for(let j=0;j<14;j++){
temp[j] = colors[Math.floor(Math.random() * 6)]
}
dp.push(temp)
}
var [state, setState] = useState({
boardColors: dp,
covered : [{x:0,y:0}],
})
var changeState = (color) => {
var prevcolor = state.boardColors[0][0]
var q = [...state.covered]
var append_arr = [...state.covered]
function isnotcovered(l,m,xt){
for(let i=0;i<xt.length;i++){
if(xt[i].x===l && xt[i].y===m){
return false;
}
}
return true;
}
var temp2D = [...state.boardColors];
while(q.length){
let top = q.shift()
let l = top.x;
let m = top.y;
if( m-1>0 && temp2D[l][m-1]===prevcolor && isnotcovered(l,m-1,append_arr)){
q.push({x:l,y:m-1})
append_arr.push({x:l,y:m-1})
}
if(l+1<14 && temp2D[l+1][m]===prevcolor && isnotcovered(l+1,m,append_arr)){
q.push({x:l+1,y:m})
append_arr.push({x:l+1,y:m})
}
if(m+1<14 && temp2D[l][m+1]===prevcolor && isnotcovered(l,m+1,append_arr)){
q.push({x:l,y:m+1})
append_arr.push({x:l,y:m+1})
}
if(l-1>0 && temp2D[l-1][m]===prevcolor && isnotcovered(l-1,m,append_arr)){
q.push({x:l-1,y:m})
append_arr.push({x:l-1,y:m})
}
}
setState((state) => ({boardColors : temp2D, covered : [...state.covered, ...append_arr]}) )
setState((state) => updateState(state))
function updateState(state){
function isnotcovered(l,m,xt){
for(let i=0;i<xt.length;i++){
if(xt[i].x===l && xt[i].y===m){
return false;
}
}
return true;
}
var temp2D = [...state.boardColors];
for(let i=0; i<state.covered.length; i++){
temp2D[state.covered[i].x][state.covered[i].y]=color
}
var q = [...state.covered]
var append_arr = [...state.covered]
while(q.length){
let top = q.shift()
let l = top.x;
let m = top.y;
if( m-1>0 && temp2D[l][m-1]===color && isnotcovered(l,m-1,append_arr)){
q.push({x:l,y:m-1})
append_arr.push({x:l,y:m-1})
}
if(l+1<14 && temp2D[l+1][m]===color && isnotcovered(l+1,m,append_arr)){
q.push({x:l+1,y:m})
append_arr.push({x:l+1,y:m})
}
if(m+1<14 && temp2D[l][m+1]===color && isnotcovered(l,m+1,append_arr)){
q.push({x:l,y:m+1})
append_arr.push({x:l,y:m+1})
}
if(l-1>0 && temp2D[l-1][m]===color && isnotcovered(l-1,m,append_arr)){
q.push({x:l-1,y:m})
append_arr.push({x:l-1,y:m})
}
}
return {
boardColors : temp2D,
covered : [...state.covered, ...append_arr],
}
}
}
return (
<>
<Board colors2D={state.boardColors} />
<Controls boardColors={state.boardColors} colors2D={state.boardColors} colors={colors} color='green' changeState={changeState}/>
</>
);
}
function Board(props){
var boardStyle = {
height: '280px',
aspectRatio: '1',
backgroundColor: 'lightgreen'
}
var display = props.colors2D.map((color1D, index1) => (color1D.map((color,index2) => <div key={14*index1 + index2} style={{width:'20px', aspectRatio: '1', backgroundColor: color, float: 'left'}}></div>)))
return(
<div style={boardStyle}>
{display}
</div>
)
}
function Controls(props){
var controlStyle = {
height: '200px',
width: '300px',
backgroundColor: props.color
}
var handleClick = (color) => {
props.changeState(color)
}
var buttons = props.colors.map((color, index) => <button key={index} onClick={() => handleClick(color)}>{color}</button>)
return(
<>
<div style={controlStyle}>
{buttons}
</div>
</>
)
}
export default App;
replace this code in App.js and it should work fine

I think I've found the problem by doing a console.log of your state everytime it changes:
useEffect(() => {
console.log(state);
}, [state]);
Everytime you click on a color button (even if it's the same), the displayed state shows an array as value of the covered key that gets its size multiplied by 4.
This is because you are copying the content of covered boxes in append_arr variable:
var append_arr = [...state.covered]
And when the computing is done you append the content of state.covered and the content of append_arr into state.covered:
covered : [...state.covered, ...append_arr],
This makes it at least double at every click.
You should assign only the result of append_arr as the new covered state:
covered : [...append_arr],
And the reason it actually quadruples is because you are doing this twice, with 2 functions changeState and updateState doing more or less the same thing and called each time.
You are callign setState twice. This can't be useful because the result of the first call will be applied only when the whole function finishes. You should remove one of the occurrences:
/* removing this one
setState((state) => ({boardColors : temp2D, covered : [...state.covered, ...append_arr]}) ) */
setState((state) => updateState(state))

Related

script time out, even when there seems to be no issue with code

I am a new to this and it is becoming frustrating to be stuck for the past couple of days I would be glad to have your inputs on my piece of code, it is a google script that has run well for quite some time but i guess the data is becoming bigger and my function times out every time it is triggered. I can not seem to understand how to optimise it.
I have a loop where iterations happen and I am looking for ways to reduce th run time
type here
function Serio() {
const sss = SpreadsheetApp.getActiveSpreadsheet();
const ssSetting = sss.getSheetByName('Labs');
var tss = sss.getSheetByName("Finished");
var clearseriosh = sss.getSheetByName("Serio");
clearseriosh.getRange(2, 1,clearseriosh.getLastRow(), clearseriosh.getLastColumn()).clearContent();
const serioLabs = tss.getRange("M2:M").getValues();
const seriotag = tss.getRange("J2:J").getValues();
const serioprod = tss.getRange("L2:L").getValues();
const ts = sss.getSheetByName('Serio');
for (var i = 0; i < seriolabs.length; i++) {
try{
if(seriolabs[i][0] != '' &&seriolabs [i][0] != '#REF'){
var shellunitlink = seriolabs[i][0];
var shellunitId = shellunitlink.substring(xx,xx);
var tag = seriotag[i];
var prod = serioprod [i];
const sourcesheetSerio =  SpreadsheetApp.openById(shellunitId);   
const sourceSerio = sourcesheetSerio.getSheetByName("Arrivals");
const ts = sss.getSheetByName('Serio');
if(sourceSerio){
const rawData = sourceSerio.getRange("C2:I").getValues().map(([
A,B,C,D,E,F,G,H,I]) => ["Serio",tag,prod,A,B,C,D,E,F,G,H,I,shellunitlink]);
const rrawData=rawData.filter(row=>row[0]=="Serio" && row[1]==tag && row[2]==prod && row[3]!=""&& row[4]=="toxic");
if(rrawData.length > 0 ){
ts.getRange(ts.getLastRow()+1, 1, rrawData.length,13).setValues(rrawData);
}
const rrawData1=rawData.filter(row=>row[0]=="Serio" && row[1]==tag && row[2]==prod && row[3]!=""&& row[4]=="NA");
if(rrawData1.length > 0 ){
ts.getRange(ts.getLastRow()+1, 1, rrawData1.length,13).setValues(rrawData1);
}
const rrawData2=rawData.filter(row=>row[0]=="Serio" && row[1]==tag && row[2]==prod && row[3]!=""&& row[4]=="eco");
if(rrawData2.length > 0 ){
ts.getRange(ts.getLastRow()+1, 1, rrawData2.length,13).setValues(rrawData2);
}
}
}}
catch(e) {
continue;//If error continue looping
}}
}
I'm not able to test it, and I hope I caught all my typos, etc. But I think this will work.
I typically use indices into the value array rather than define individual varaibles.
First, I get all the values from sheet into seriosValues, remove the header.
Then, I filter each sheets column A !== "". Then I concatinate the tag and prod and to the row values. Filter each "toxic", "NA" and "eco" group. The catch return is the same as continue in a for loop.
Now that I have collected all rows into rrawData I write to the spreadsheet.
function Serio() {
let sss = SpreadsheetApp.getActiveSpreadsheet();
let ssSetting = sss.getSheetByName('Labs');
let tss = sss.getSheetByName("Finished");
let ts = sss.getSheetByName("Serio");
ts.getRange(2, 1,ts.getLastRow(), ts.getLastColumn()).clearContent();
let seriosValues = tss.getDataRange().getValues();
seriosValues.shift(); // remove headers
let rawData = [];
let rrawData = [];
seriosValuesforEach( seriosRow => {
if( ( seriosRow[0] !== "" ) && ( seriosRow[0] !== "#REF" ) ) {
let shellunitId = seriosRow[0].substring(xx,xx);
try {
let sourcesheetSerio = SpreadsheetApp.openById(shellunitId);
let sourceSerio = sourcesheetSerio.getSheetByName("Arrivals");
if( sourceSerio ) {
rawData = sourceSerio.getRange(2,3,sourceSerio.getLastRow()-1,9).getValues();
rawData = rawData.filter( rawRow => rawRow[0] !== "" ); // filter A !== ""
rawData = rawData.map( rawRow => ["Serio",tag,prod].concat(rawRow,seriosRow[0]) );
// all rows or rawData have "Serio",tag,prod
rawData.forEach( rawRow => {
if( rawRow[4] === "toxic" ) rrawData.push(rawRow);
}
);
rawData.forEach( rawRow => {
if( rawRow[4] === "NA" ) rrawData.push(rawRow);
}
);
rawData.forEach( rawRow => {
if( rawRow[4] === "eco" ) rrawData.push(rawRow);
}
);
}
}
catch(err) {
Logger.log("Error in Serio: "+err);
return;
}
}
}
);
if( rrawData.length > 0 ) {
ts.getRange(ts.getLastRow()+1, 1, rrawData.length,13).setValues(rrawData);
}
}

How to automatically place the mouse cursor in an input at a precise index outside the callback() function with useRef?

I have an input in which the user enters an expression of this form:
"TERM":"MATCH_TERM"
I would like that when the user enters a quote in the input, a second quote of the same type is added (the user can use single and double quotes : " or ') and that the mouse cursor is placed between the two quotes that have just been created.
My code is in ReactJs.
I managed to automatically add a second quote when the user enters a first one, at the right place in the string. But I can't figure out how to then move my mouse cursor between the two new quotes.
To make my input component do this, I wrote the following code:
(I tried to simplify the code but normally it is reproducible)
import * as React from "react";
import { useEffect, useState, useRef } from "react";
const QuoteInput: React.FC = () => {
const inputRef = useRef<HTMLInputElement | null>(null);
const [inputChoose, setInputChoose] = useState("");
const [previousInputChoose, setPreviousInputChoose] = useState("");
const [testQuoteAddition, setTestQuoteAddition] = useState(false);
const [enterSingleQuote, setEnterSingleQuote] = useState(false);
const [enterDoubleQuote, setEnterDoubleQuote] = useState(false);
const inputHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
setPreviousRequestChoose(requestChoose);
const enteredRequest = event.target.value;
setRequestChoose(enteredRequest);
setTestQuoteAddition(true);
};
function addSingleQuote(indexDifference: number) {
let newString: string = requestChoose.slice(0,indexDifference + 1) + "'" + requestChoose.slice(indexDifference + 1);
setRequestChoose(newString);
if(inputRef !== null && inputRef !== undefined) {
if (inputRef.current !== null && inputRef.current !== undefined) {
console.log("3 ");
if (inputRef.current.setSelectionRange !== undefined) {
inputRef.current.setSelectionRange(indexDifference, indexDifference);
}
}
}
}
function addDoubleQuote(indexDifference: number) {
let newString: string = requestChoose.slice(0,indexDifference + 1) + '"' + requestChoose.slice(indexDifference + 1);
setRequestChoose(newString);
}
useEffect(()=>{
if(testQuoteAddition === true) {
for(let i=0; i<requestChoose.length; i++) {
if(previousRequestChoose.charAt(i) !== requestChoose.charAt(i))
{
if (requestChoose.charAt(i) === "'") {
setEnterSingleQuote(true);
} else if (requestChoose.charAt(i) === '"') {
setEnterDoubleQuote(true);
}
}
}
}
setTestQuoteAddition(false);
},[testQuoteAddition])
useEffect(()=>{
if(enterSingleQuote === true){
let indexDifferenceInRequest: number = requestChoose.length + 1;
let findDifference: boolean = false
for(let i=0; i<requestChoose.length; i++) {
if(previousRequestChoose.charAt(i) !== requestChoose.charAt(i) && findDifference === false)
{
indexDifferenceInRequest = i;
findDifference = true;
}
}
addSingleQuote(indexDifferenceInRequest);
setEnterSingleQuote(false);
} else if (enterDoubleQuote === true){
let indexDifferenceInRequest: number = requestChoose.length + 1;
let findDifference: boolean = false
for(let i=0; i<requestChoose.length; i++) {
if(previousRequestChoose.charAt(i) !== requestChoose.charAt(i) && findDifference === false)
{
indexDifferenceInRequest = i;
findDifference = true;
}
}
addDoubleQuote(indexDifferenceInRequest);
setEnterDoubleQuote(false);
}
},[enterSingleQuote, enterDoubleQuote])
return(
<div>
<input ref={inputRef} type="text" onChange={inputHandler} value={inputChoose} className="text-center" placeholder="enter an input" />
</div>
);
}
export default QuoteInput;
This code allows me to add a new pair of quotes but the mouse cursor is then placed at the end of the string.
If I put: inputRef.current?.setSelectionRange(3, 3); in the inputHandler (the callback function of my input element), every time the user writes the cursor is reset to the third position of the string.
But if I put this same line: inputRef.current?.setSelectionRange(indexDifference, indexDifference); in the function which add a quote, as in the code above, nothing happens, but if I put console.log in the loop it displays well so my conditions are well met and the statement should execute.
I don't see what I'm doing wrong at all, if you could point me in the right direction it would help me a lot.
I just noticed another problem with my add quote function while I was writing this question.
When I type at normal speed in the search bar everything works normally. But if I suddenly decide to write super fast, the function of adding a quote when a user has put a first one stops working and doesn't work at all afterwards even if I start writing again at a normal speed.
Once the users knows how to used my tool well, it may happen that they type very fast and so this use case may occur.
When I display my state variables with console.log, it seems that before typing very fast, everything is triggered in the inputHandler. But after typing very fast, only state variables concerning the old string and the new string entered by the user are activated, the boolean state variable to launch the verification test of adding a new quote is no longer activated and therefore never goes to true (It should be set to true in inputHandler (the callback function of the input element in the return()) which then enables the first useEffect since the boolean state variable is in the dependency array.).
Does anyone know what can cause this behavior between an input, its callback function and the state variables?
If anyone understands my problem and is willing to help me it would be a great help.
I didn't find the answer to my second question (stopping the feature from working if the user spams the input with a lot of characters) but I finally found my problem. I was missing a useEffect for my previous code to work normally, because of that, one of my state variables didn't have time to update and this creates the strange behaviors of repositioning the cursor in the input.
In case someone is interested I put the correct code here (I added some validation conditions in some useEffects to avoid bugs of randomly adding quotation marks especially when copying and pasting):
import * as React from "react";
import { useEffect, useState, useRef } from "react";
const QuoteInput: React.FC = () => {
const inputRef = useRef<HTMLInputElement | null>(null);
const [inputChoose, setInputChoose] = useState("");
const [previousInputChoose, setPreviousInputChoose] = useState("");
const [testQuoteAddition, setTestQuoteAddition] = useState(false);
const [enterSingleQuote, setEnterSingleQuote] = useState(false);
const [enterDoubleQuote, setEnterDoubleQuote] = useState(false);
const [writeNewSingleQuote, setWriteNewSingleQuote] = useState(false);
const [writeNewDoubleQuote, setWriteNewDoubleQuote] = useState(false);
const [repositionCaretBetweenQuotes, setRepositionCaretBetweenQuotes] = useState(false);
const [indexDifferenceInInput, setIndexDifferenceInInput] = useState(0);
const inputHandler = (event: React.ChangeEvent<HTMLInputElement>) => {
setPreviousRequestChoose(requestChoose);
const enteredRequest = event.target.value;
setRequestChoose(enteredRequest);
setTestQuoteAddition(true);
};
useEffect(()=>{
if(testQuoteAddition === true && (previousInputChoose.length === inputChoose.length || previousInputChoose.length === inputChoose.length - 1)) {
for(let i=0; i<=inputChoose.length; i++) {
if(previousInputChoose.charAt(i) !== inputChoose.charAt(i))
{
let previousSingleQuoteNumber = previousRequestChoose.split("'").length - 1;
let previousDoubleQuoteNumber = previousRequestChoose.split('"').length - 1;
let previousQuoteNumber: number;
if (previousSingleQuoteNumber !== undefined && previousDoubleQuoteNumber !== undefined) {
previousQuoteNumber = previousSingleQuoteNumber + previousDoubleQuoteNumber;
} else if (previousSingleQuoteNumber !== undefined && previousDoubleQuoteNumber === undefined) {
previousQuoteNumber = previousSingleQuoteNumber;
} else if (previousSingleQuoteNumber === undefined && previousDoubleQuoteNumber !== undefined) {
previousQuoteNumber = previousDoubleQuoteNumber;
} else {
previousQuoteNumber = 0;
}
previousQuoteNumber = previousSingleQuoteNumber! + previousDoubleQuoteNumber!;
let currentSingleQuoteNumber = requestChoose.split("'").length - 1;
let currentDoubleQuoteNumber = requestChoose.split('"').length - 1;
let currentQuoteNumber: number;
if (currentSingleQuoteNumber !== undefined && currentDoubleQuoteNumber !== undefined) {
currentQuoteNumber = currentSingleQuoteNumber + currentDoubleQuoteNumber;
} else if (currentSingleQuoteNumber !== undefined && currentDoubleQuoteNumber === undefined) {
currentQuoteNumber = currentSingleQuoteNumber;
} else if (currentSingleQuoteNumber === undefined && currentDoubleQuoteNumber !== undefined) {
currentQuoteNumber = currentDoubleQuoteNumber;
} else {
currentQuoteNumber = 0;
}
currentQuoteNumber = currentSingleQuoteNumber! + currentDoubleQuoteNumber!;
if (inputChoose.charAt(i) === "'" && currentQuoteNumber !== previousQuoteNumber) {
setEnterSingleQuote(true);
} else if (inputChoose.charAt(i) === '"' && currentQuoteNumber !== previousQuoteNumber) {
setEnterDoubleQuote(true);
}
}
}
}
setTestQuoteAddition(false);
},[testQuoteAddition])
useEffect(()=>{
if(enterSingleQuote === true){
let findDifference: boolean = false
for(let i=0; i<inputChoose.length; i++) {
if(previousInputChoose.charAt(i) !== inputChoose.charAt(i) && findDifference === false)
{
setIndexDifferenceInInput(i);
findDifference = true;
}
}
setEnterSingleQuote(false);
setWriteNewSingleQuote(true);
} else if (enterDoubleQuote === true){
let findDifference: boolean = false
for(let i=0; i<inputChoose.length; i++) {
if(previousInputChoose.charAt(i) !== inputChoose.charAt(i) && findDifference === false)
{
setIndexDifferenceInInput(i);
findDifference = true;
}
}
setEnterDoubleQuote(false);
setWriteNewDoubleQuote(true);
}
},[enterSingleQuote, enterDoubleQuote])
useEffect(()=>{
if (writeNewSingleQuote === true) {
let newString: string = inputChoose.slice(0,indexDifferenceInInput + 1) + "'" + inputChoose.slice(indexDifferenceInInput + 1);
setInputChoose(newString);
if(inputRef !== null && inputRef !== undefined) {
if (inputRef.current !== null && inputRef.current !== undefined) {
if (inputRef.current.setSelectionRange !== undefined) {
setRepositionCaretBetweenQuotes(true);
}
}
}
} else if (writeNewDoubleQuote === true) {
let newString: string = inputChoose.slice(0,indexDifferenceInInput + 1) + '"' + inputChoose.slice(indexDifferenceInInput + 1);
setInputChoose(newString);
if(inputRef !== null && inputRef !== undefined) {
if (inputRef.current !== null && inputRef.current !== undefined) {
if (inputRef.current.setSelectionRange !== undefined) {
setRepositionCaretBetweenQuotes(true);
}
}
}
}
},[writeNewSingleQuote, writeNewDoubleQuote])
useEffect(()=>{
setWriteNewSingleQuote(false);
setWriteNewDoubleQuote(false);
if (repositionCaretBetweenQuotes === true) {
inputRef?.current?.setSelectionRange(indexDifferenceInInput + 1, indexDifferenceInInput + 1);
}
setRepositionCaretBetweenQuotes(false);
},[repositionCaretBetweenQuotes])
return(
<div>
<input ref={inputRef} type="text" onChange={inputHandler} value={inputChoose} className="text-center" placeholder="enter an input" />
</div>
);
}
export default QuoteInput;

Use state in React causing value of const to break

I'm trying to make an app where the user can cycle through a pack of cards. I'm trying to make it so each card is unique so that a user cannot pick two of the same card consecutively. Therefore the way I thought of doing this was by adding all the cards to an array and then shuffling that array.
import * as React from "react";
import { View, Text, Button } from "react-native";
import homeStyles from "../styles/homeStyles";
import { cardsList } from "../assets/cardsList";
import { useEffect, useState } from "react";
export default function Home() {
const [CurrentQuestion, setCurrentQuestion] = useState(1);
const [usedQuestions, setUsedQuestions] = useState([]);
const test = [];
const length = cardsList.length;
const shuffle = () => {
let i,
x = 0,
y = 0,
temp0 = 0;
for (i = 0; i < usedQuestions.length; i++) {
x = Math.floor(Math.random() * usedQuestions.length);
y = Math.floor(Math.random() * usedQuestions.length);
if (x === y) {
//for dont change arr[index] with self !!!
continue;
}
temp0 = usedQuestions[x];
usedQuestions[x] = usedQuestions[y];
usedQuestions[y] = temp0;
}
console.log("Shuffled : " + usedQuestions);
};
let i = 0;
const nextQuestion = () => {
let plz = null;
if (i <= usedQuestions.length) {
// plz = usedQuestions[i]
setCurrentQuestion(usedQuestions[i]);
i = i + 1;
} else {
i = 0;
// shuffle()
// nextQuestion()
}
// console.log(plz)
console.log(CurrentQuestion);
};
const start = () => {
let i = 1;
while (i <= length) {
test.push(i);
i++;
}
console.log("initial : " + test);
setUsedQuestions([...usedQuestions, ...test]);
};
return (
<View style={homeStyles.container}>
<View style={homeStyles.card}>
{cardsList
.filter(function (item) {
return item.id === CurrentQuestion;
})
.map((cardsList) => {
return (
<View key={cardsList.id}>
<View style={homeStyles.line}>
<Text style={homeStyles.cardNumber}>
{cardsList.id}) {cardsList.title}
</Text>
</View>
<Text style={homeStyles.cardText}>{cardsList.cardText}</Text>
</View>
);
})}
<Button
style={homeStyles.button}
title="Shuffle"
onPress={() => shuffle()}
/>
<Button
style={homeStyles.button}
title="Next Question"
onPress={() => nextQuestion()}
/>
<Button
id="test"
style={homeStyles.button}
title="Start"
onPress={() => start()}
/>
</View>
</View>
);
}
The section below is the section causing me issues
let i = 0;
const nextQuestion = () => {
let plz = null;
if (i <= usedQuestions.length) {
// plz = usedQuestions[i]
setCurrentQuestion(usedQuestions[i]);
i = i + 1;
} else {
i = 0;
// shuffle()
// nextQuestion()
}
// console.log(plz)
console.log(CurrentQuestion);
};
The const plz works perfectly returning the value of the cards one by one from the shuffled array. See Image
However when I try and use the UseState feature from react to set this value so that I can use it to display the correct part in the app section it breaks.
Any idea why this happens and how I can fix it? I'm fairly new to coding so any tips on how I could better it would be appreactiated.
You cannot modify a state directly
You can create a copy, shuffle it and reassign the state in this way
const shuffle = () => {
setUsedQuestion(u => {
const data = [...u]
data.sort(() => Math.random() - 0.5))
console.log("Shuffled : " + data);
return data;
}
)
}
Everytime you update a react state (eg. by calling setCurrentQuestion) your component will rerender.
Here, rerendering means it will execute the code of your function component again (function Home()).
In the body of function Home() you have this: let i = 0; so i will reset everytime you change a local state.
Now what you probably want is to persist the current question index across multiple rerenders.
you could do nextQuestion like this:
// remove: const [CurrentQuestion, setCurrentQuestion] = useState(1);
const [currentQuestionIndex, setCurrentQuestionIndex] = useState(0);
const currentQuestion = usedQuestions[currentQuestionIndex];
const nextQuestion = () => {
if (currentQuestionIndex < usedQuestions.length) {
setCurrentQuestionIndex(currentQuestionIndex + 1);
} else {
setCurrentQuestionIndex(0);
}
};
You might have more tweaking to do but that should get you started

React component not rerendering based on props change. JSX in async function

I have a filterTime state which is set to "Daily" as default. It is located at the parent of Friends.js and passed as prop.
Friends.js
const Friends = (props) =>{
const [friendsDisplay, setFriendsDisplay] = useState([]);
let friends = [];
useEffect(()=>{
fetchData();
}, [])
const fetchData = async() => {
try{
let doc = await docRef.get();
let data = doc.data();
for(let i of data.Friends){ //Getting the array of friends' uids
friends.push(i);
}
friends = await Promise.all(friends.map(async UserFriend => {
if(UserFriend !== ""){
let friend = await fetchUserFriendData(UserFriend);
return (<Friend key = {UserFriend}
UserFriend = {friend}
isUser = {false}
src = {``}
filterTime = {props.filterTime}/>
)
}
return null;
}));
let userInfo = <Friend key = {uid}
UserFriend = {data}
isUser = {true}
src = {``}
filterTime = {props.filterTime}/>
friends = friends.concat(userInfo);
sortFriends();
}catch(error){
console.log(error);
}
}
const sortFriends = () =>{
if(friends.length !== 0){ //If User Has Friends Sorts Them in Descending Order
friends.sort((a, b) => {
if(props.filterTime === "Daily"){
return (a.props.UserFriend.XpEarnedToday > b.props.UserFriend.XpEarnedToday ? -1 : 1);
}
if(props.filterTime === "Weekly"){
return (a.props.UserFriend.XpEarnedWeek > b.props.UserFriend.XpEarnedWeek ? -1 : 1);
}
if(props.filterTime === "Monthly"){
return (a.props.UserFriend.XpEarnedMonth > b.props.UserFriend.XpEarnedMonth ? -1 : 1);
}
return -1;
});
setFriendsDisplay(friends);
}
return (
<>
<div className = "friendsContainer">
{friendsDisplay.length !== 0 && //If User Has Friends (ScoreBoard)
<div className = "friends">{friendsDisplay}</div>
}{friendsDisplay.length === 1 && //If User Has No Friends (Add Friend Button)
<>{friendsDisplay}</>
}
</div>
</>
);
}
Friend.js
const Friend = (props) =>{ //filterTime---> Daily Weekly etc. UserFriend--->all properties of friend isUser--->boolean src--->source for picture
const[friendName, setFriendName] = useState(props.UserFriend.Name);
let WindowWidth = windowResizeListener();
return (
<div className = {`friendBackground ${props.isUser ? "friendBackground-User" : ""}`}>
<div className = "friendPorperties">
<p className = {`friendRank ${props.isUser ? "friendRank-User" : ""}`}>1</p>
<p className = {`friendName ${props.isUser ? "friendName-User" : ""}`}>{friendName}</p>
</div>
{props.filterTime === "Daily" && //checks if filter = daily
<p className = {`friendXp ${props.isUser ? "friendXp-User" : ""}`}>{`+${props.UserFriend.XpEarnedToday}Xp`}</p>
}
{props.filterTime === "Weekly" && //checks if filter = weekly
<p className = {`friendXp ${props.isUser ? "friendXp-User" : ""}`}>{`+${props.UserFriend.XpEarnedWeek}Xp`}</p>
}
{props.filterTime === "Monthly" && //checks if filter = monthly
<p className = {`friendXp ${props.isUser ? "friendXp-User" : ""}`}>{`+${props.UserFriend.XpEarnedMonth}Xp`}</p>
}
</div>
);
}
When I use;
useEffect(()=>{
console.log(props.filterTime);
}, [props.filterTime])
In Friends.js it logs the correct filterTime value as I click different filters. Whereas in Friend.js it is stuck in the first value ("Daily") and won't update if I click different filters.
So the problem isn't about filterTime it is about child not rerendering as the filterTime value changes.
React Dev tools also shows prop.filterTime isn't changing for Friend.js.
How can I fix this.

Use a prop function inside useEffect return: prop.function is not a function

I am currently trying to build an next.js app.
So what I want to do is passing a function to a component and call it with useEffect
I have the following component in order to change the props of the parents element. Therefore I pass the function from the parent like this:
<NumberInput name="height" update={manuelUpdate}></NumberInput>
The manuelUpdate function is a another props function from the actual parent:
const manuelUpdate = (name, value) => {
props.update(name, value);
};
It works fine if I run the props.function with the onclick functions. However as soon as I try to use it in useEffect, it returns that the function is not a function.
Maybe I am just thinking to complicated..
this is the component;
const NumberInput = ({ name, min = undefined, max = undefined, ...props }) => {
const minInput = min !== undefined
? min
: null;
const maxInput = max !== undefined
? max
: null;
const [numb, setNumb] = useState(0);
useEffect(() => {
console.log(props.update)
}, [numb]);
const increaseNumb = () => {
if (numb < maxInput || maxInput == null) {
setNumb(numb + 1)
}
props.update(name, numb)
};
const decreaseNumb = () => {
if (numb < minInput || minInput == null) {
setNumb(numb - 1)
}
};
const changeHandler = ({ e }) => {
let n = parseInt(e.target.value)
if (Number.isNaN(n)) {
setNumb(numb)
} else {
setNumb(n)
}
}
return (
<div className={styles.def_number_input, styles.number_input}>
<button onClick={decreaseNumb} className={styles.minus}></button>
<input className={styles.quantity} name="quantity" value={numb} onChange={(e) => changeHandler({ e })}
type="number" />
<button onClick={increaseNumb} className={styles.plus}></button>
</div>
);
};
sorry in advance if my question is stupid or my code messy, I am still in the process of learning :D
Issue was related to another problem. I was accidentally calling the component 2 times and forgot to adjust the parameters. CASE CLOSED

Categories

Resources