My React code get crash some time but not always - javascript

I am building a project in which there is only a single question and it changes when pressing the refresh button. Everything is going well but sometimes the code crashes and the page gets blank (as shown in attached image) on pressing the refresh button.
import React from "react"
import questionsData from '../questionsData.js'
function Content(){
const [question, setQuestion] = React.useState({
heading:"",
hints : []
})
const [refresh, setRefresh] = React.useState(0)
React.useEffect(function(){
if(questionsData){
const randomNo = Math.floor(Math.random() * questionsData.length) + 1
let headinggg = questionsData[randomNo].heading
let hintsss = questionsData[randomNo].hints
setQuestion({
heading: headinggg,
hints: hintsss
})
}else{
document.write("loading")
}
},[refresh])
const sugg = question.hints.map((hint) => {
return(
<li>{hint}</li>
)
})
function renderQuestion(){
setRefresh(refresh + 1)
}
return(
<div className="content">
<div className="question">
<h4>{question.heading}</h4>
<br />
<span>You should say:</span>
<ul>
{sugg}
</ul>
</div>
<div className="action">
<div className="record">
<img src="images/211859_mic_icon.svg" alt="" />
</div>
<div className="refresh" onClick={renderQuestion}>
<img src="images/3994399_refresh_reload_repeat_rotate_sync_icon.svg" alt="" />
</div>
</div>
</div>
)
}
export default Content

You should remove the + 1 from the randomNo calculation, otherwise it could case an IndexOutOfBounds error:
const randomNo = Math.floor(Math.random() * questionsData.length);

Related

H1 tag value not showing when page loads up in React

Problem: when the page first loads up, the PageHeader H1 tag shows a blank value instead of "New Page". Why does this happen and how can I make it show "New Page"?
In Page.js
const [pageTitle, setPageTitle] = useState('New Page');
const pageTitleChangeHandler = (e) => {
setPageTitle(e.target.innerText);
}
return (
<PageHeader title = {pageTitle} onChange = {pageTitleChangeHandler}></PageHeader>
);
In PageHeader.js
return (
<div className = "page-header">
<h1
className = "page-header-text"
contentEditable = "true"
onInput = {props.onChange}
value = {props.title}>
</h1>
</div>
);
I think you should remove value attribute from h1 tag and set dangerouslySetInnerHTML instead like
import React from "react";
export default function PageHeader(props) {
return (
<div className="page-header">
<h1
className="page-header-text"
contentEditable="true"
onInput={props.onChange}
dangerouslySetInnerHTML={{ __html: props.title }}
>
</h1>
</div>
);
}
If you want New Page to show up on page load, you need to change the argument to setPageTitle from e.target.innerText to e.target.value, and add {props.title} in the h1 tag.
Then you can change onInput to onKeyDown.
This alone would delete New Page from the header once you edit. To avoid this, simply change title={pageTitle} to title={"New Page"}.
const [pageTitle, setPageTitle] = useState('New Page');
const pageTitleChangeHandler = (e) => {
// This will set the value prop
setPageTitle(e.target.value);
}
return (
<PageHeader
title={"New Page"}
onChange={pageTitleChangeHandler}
></PageHeader>
);
}
const PageHeader = (props) => {
return (
<div className="page-header">
<h1
className="page-header-text"
contentEditable="true"
onKeyDown={props.onChange}
value={props.title}
>
{props.title}
</h1>
</div>
);

TypeError: Cannot read properties of undefined (reading 'style') when the setTimeout run

I am building a image slider, that change of image if the user after 5s donĀ“t use it.
And when the 5s second pass the error is display.
Thank you for your help
import React, { useRef, useState } from 'react'
import BeforeIcon from "../assests/before.svg"
import NextIcon from "../assests/next.svg"
import Banner1 from "../assests/banner1.jpg"
import Banner2 from "../assests/banner2.jpg"
import "../styles/banner.css"
export default function Banner() {
const ImgContainer = useRef()
const [position, setposition] = useState(0)
const w = window.innerWidth
const handleNext = () => {
let newposition = position+1
let move = w*newposition
setposition(newposition)
return(
ImgContainer.current.style.transform = "translateX(-" + move + "px)",
ImgContainer.current.style.transition = "transform, 1s",
console.log(move),
setposition(position+1)
)
}
const handleBefore = () => {
let newposition = position-1
let move = w*newposition
setposition(newposition)
return(
ImgContainer.current.style.transform = "translateX(-" + move + "px)",
ImgContainer.current.style.transition = "transform, 1s",
console.log(ImgContainer.current.style)
)
}
setTimeout(() => {
if (position===0){
handleNext()
}
else {
return(
console.log("slide")
)
}
}, 5000);
return (
<section>
<button onClick={handleBefore}><img src={BeforeIcon} alt="" className="button rigth"/></button>
<div ref={ImgContainer} className="imgcontainer">
<img src={Banner2} alt="" className="bannerimg"/>
<img src={Banner2} alt="" className="bannerimg"/>
<img src={Banner2} alt="" className="bannerimg"/>
<img src={Banner2} alt="" className="bannerimg"/>
<img src={Banner1} alt="" className="bannerimg"/>
<img src={Banner1} alt="" className="bannerimg"/>
<img src={Banner1} alt="" className="bannerimg"/>
</div>
<button onClick={handleNext}><img src={NextIcon} alt="" className="button left"/></button>
</section>
)
}
setTimeout doesn't play very well in React components. I would recommend taking a look at this article by Josh Comeau he has a far better explanation and solve for setTimeout than what I could provide.

react function- infinite loop - Minified React error #301

I want to initialize Cards by calling initializeInput. but it is getting called in loop and goes out of memory. But if i call same function on button click Reset it works fine.
How can i call initializeInput on start up ?
const CardsContainer = (props) => {
var inputNum1, inputNum2, inputNum3;
const [inputNumber, setInputNumber] = React.useState([inputNum1,inputNum2,inputNum3 ]);
const initializeInput = React.useCallback(() => {
inputNum1 = Math.floor(Math.random() * 10) + 1;
inputNum2 = Math.floor(Math.random() * 10) + 1;
inputNum3 = Math.floor(Math.random() * 10) + 1;
console.log("Inside initialize", inputNum1, inputNum2, inputNum3);
setInputNumber([inputNum1, inputNum2, inputNum3]);
}, [inputNum1, inputNum2, inputNum3]);
// *initializeInput(); --> if i include gives out of memory*
return (
<div class="Container">
<Cards inputNum={inputNumber[0]} />
<Cards inputNum={inputNumber[1]} />
<Cards inputNum={inputNumber[2]} />
<button onClick={initializeInput}>Reset </button>
</div>
);
};
ReactDOM.render(<CardsContainer />, document.querySelector("#root"));
Check useEffect hook to trigger something only the first time you mount a component.
Your problem here is you are calling initializeInput that is calling setInputNumber triggering a rerender and so on...

How to replace item while scrolling?

I need to change navbar when scrolling a page. How to catch the moment when to change it? How to do it right, in accordance with the concepts of React? As far as I know, use getElementById is that bad tone?
const useState = React.useState
const useEffect = React.useEffect
const Component = () => {
const [topNavbarHide, setTopNavbarHide] = useState(true);
useEffect(() => {
window.addEventListener('scroll', function () {
let navbarSize = document.getElementById('navbar').offsetHeight;
console.log("navbarSize " + navbarSize + "px");
let scrollTop = document.documentElement.scrollTop;
console.log("scrollTop " + scrollTop);
if (scrollTop > navbarSize) {
setTopNavbarHide(false)
} else {
setTopNavbarHide(true)
}
console.log(topNavbarHide);
});
});
return (
<div>
<div id={"navbar"} className="navbar">
<div
className={(topNavbarHide) ? "topNavbar" : "topNavbar hide"}>topNavbar
</div>
<div className="bottomNavbar">bottomNavbar</div>
</div>
<div className="box"></div>
<div className="box1"></div>
<div className="box2"></div>
<div className="box"></div>
<div className="box1"></div>
</div>
)
};
ReactDOM.render(
<Component/>,
document.getElementById('root')
);
https://codepen.io/slava4ka/pen/wvvGoBX
It is perfectly fine to add an event listener, in the way that you are currently doing it, to a React hook. The way that you are doing it is the correct way.
There is another, more simpler way with react-waypoint. You can place invisible waypoints on your screen and it can trigger events when waypoint enter or leave screen.
For example:
const Component = () => {
const [topNavbarHide, setTopNavbarHide] = useState(true);
return (
<div>
<div id={"navbar"} className="navbar">
<div className={topNavbarHide ? "topNavbar" : "topNavbar hide"}>
topNavbar
</div>
<div className="bottomNavbar">bottomNavbar</div>
</div>
<Waypoint
onEnter={() => setTopNavbarHide(true)}
onLeave={() => setTopNavbarHide(false)}
/>
<div className="box" />
<div className="box1" />
<div className="box2" />
<div className="box" />
<div className="box1" />
</div>
);
};
It is basically working like your example.
https://codesandbox.io/s/hungry-hodgkin-5jucl

Why can I only fire this function once?

I have this function:
const sliderTextChange = document.getElementsByClassName('slider') // text change
const changeSliderText = change => {
const sliderLeft = document.getElementsByClassName('switch-left')
const sliderRight = document.getElementsByClassName('switch-right')
for (let i = 0; i < change.length; i++) {
change[i].addEventListener('click', () => {
sliderRight[i].style.display = 'flex';
sliderLeft[i].style.display = 'none';
});
}
}
changeSliderText(sliderTextChange);
This is one of the many sliders on the website:
<div class="flex-column">
<h3>Text Colour</h3>
<div class="slider">
<div class="slider-back"></div>
<div class="slider-circle"></div>
</div>
<h3 class="switch-left">White</h3>
<h3 class="switch-right">Black</h3>
</div>
This function is quite a lot like many other functions in my code but they're only firing once. AKA I fire the event listener and but then I can't fire it again.
What's the issue here?
I have tried to simplify your code and keep the scope to be modular and reusable view.
function bindEvent() {
const sliderList = document.querySelectorAll('.slider');
[...sliderList].forEach((slider) => slider.addEventListener('click', () => {
const left = slider.parentElement.querySelector('.switch-left');
const right = slider.parentElement.querySelector('.switch-right');
const leftDisplay = left.style.display || 'flex';
const rightDisplay = right.style.display || 'none';
left.style.display = rightDisplay;
right.style.display = leftDisplay;
}, false));
}
window.onload = bindEvent;
<div>
<button class="slider"> - SLIDER 1 - </button>
<div class="switch-left">L</div><div class="switch-right">R</div>
</div>
<div>
<button class="slider"> - SLIDER 2 - </button>
<div class="switch-left">L</div><div class="switch-right">R</div>
</div>
<div>
<button class="slider"> - SLIDER 3 - </button>
<div class="switch-left">L</div><div class="switch-right">R</div>
</div>
<div>
<button class="slider"> - SLIDER 4 - </button>
<div class="switch-left">L</div><div class="switch-right">R</div>
</div>
Parameters you have chosen for your function are not really intuitive and make your example more complex.
We use querySelector, it's nicer to read but if you prefer speed, just go for getElementsByClassName, it also works on any DOM element.

Categories

Resources