How to avoid irresponsible app by rendering time consuming component later on - reactjs

I have a component that takes some time to be loaded (1-2 seconds), and when I switch between tabs, it takes a lot of time for the tab to be seen because it waits for that time consuming component to be fully loaded.
Here's an example:
function App() {
const [tab, setTab] = useState(1);
return (
<div>
<button onClick={() => setTab(1)}>tab 1</button>
<button onClick={() => setTab(2)}>tab 2</button>
{tab === 1 && <div>Hello World</div>}
{tab === 2 && (
<div>
<FastRenderComponent />
<FastRenderComponent />
<SlowRenderComponent />
</div>
)}
</div>
)
}
What I'd like to achieve is to be able to render the SlowRenderComponent only after the other components have been rendered so it won't take 1-2 seconds to be able to switch between tabs, but that it would be seamlessly.
How can I achieve that?
Note: I tried to wrap SlowRenderComponent with Suspense but it only worked once. An example to SlowRenderComponent:
export default function SlowRenderComponent() {
let calculation = 0;
for (let i = 0; i < 2 * 1e8; i++) {
calculation += i + Math.ceil(Math.random() * 100);
}
return <div>Slow Component, Calculation={calculation}</div>;
}

Related

state is not updating when the component re renders?

I'm making a Nextjs flashcard app. I'm passing a deck structure like this:
const deck = {
title: 'React 101',
flashcards: [flashcardOne, flashcardTwo],
};
as props to the Deck component. This component shows the first card in flashcards and a "next" button to increment the index and showing the next card in flashcards.
The Card component is very simple and shows the front and the back of the card depending of the state front.
This is what I got so far and it's working but if I click "next" when the card is showing the answer (flashcard.back), the next card is going to appear with the answer. And I'm not sure why, isn't the Card component re rendering when I click "next"? And if the component re renders, front is going to be set to true?
export default function Deck({ deck }) {
const [cardIndex, setCardIndex] = useState(0);
const { title, flashcards } = deck;
return (
<div className={styles.container}>
<main className={styles.main}>
<h1 className={styles.title}>{title}</h1>
{cardIndex < flashcards.length ? (
<>
<div className={styles.grid}>
<Card flashcard={flashcards[cardIndex]} />
</div>
<button onClick={() => setCardIndex((cardIndex) => cardIndex + 1)}>
Next
</button>
</>
) : (
<>
<div>End</div>
<button>
<Link href='/'>
<a>Go to Home</a>
</Link>
</button>
<button onClick={() => setCardIndex(0)}>Play again</button>
</>
)}
</main>
</div>
);
}
export function Card({ flashcard }) {
const [front, setFront] = useState(true);
return (
<>
{front ? (
<div
className={`${globalStyles.card} ${styles.card}`}
onClick={() => setFront(false)}
>
<p className={styles.front}>{flashcard.front}</p>
</div>
) : (
<div
className={`${globalStyles.card} ${styles.card}`}
onClick={() => setFront(true)}
>
<p className={styles.back}>{flashcard.back}</p>
</div>
)}
</>
);
}
When state changes, the card will re-render, but it will not re-mount. So, existing state will not be reset.
Call setFront(true) when the flashcard prop has changed:
const [front, setFront] = useState(true);
useLayoutEffect(() => {
setFront(true);
}, [flashcard]);
I'm using useLayoutEffect instead of useEffect to ensure front gets set ASAP, rather than after a paint cycle (which could cause flickering).
You can also significantly slim down the Card JSX:
export function Card({ flashcard }) {
const [front, setFront] = useState(true);
const face = front ? 'front' : 'back';
return (
<div
className={`${globalStyles.card} ${styles.card}`}
onClick={() => setFront(!front)}
>
<p className={styles[face]}>{flashcard[face]}</p>
</div>
);
}
Okay, I guess I had the same issue. Since you're using functional components, and you're re-using the same component or in better words, you're not unmounting and remounting the component really, you're just changing the props, this happens. For this, you need to do useEffect() and then setFront(true).
Here's the code I used in my App.
useEffect(() => {
setFront(true);
}, [flashcard]);
This is what I have used in my Word.js file.

Switch between divs when clicking prev/next buttons using React Hooks

First of all, I am really new to React, so there is a lot of stuff I have no idea about. But trying to learn I've come across Hooks, which seems really nice for a noob like me. However, my next "project" I am not quite sure about.
Basically I have 4 divs of the same size, but with different content. What I would like to do is to start at the first div (rest not visible), and then in that container I have a "Next" button, if I click that it changes/switches to the second div, which then has a "Prev" and "Next" button, and so on.
I have no idea if that is even possible with Hooks using useState or something like that.
EDIT with example:
So hopefully this illustrates my idea. And this was my initial idea. I think I may be stuck at the return where I am not sure how to actually insert that into the HTML, or what you call it in React-lingo.
const App = ( ) => {
const initialCount = 0
const [count, setCount] = useState(initialCount)
if (count = 0) {
<div className="box">
<p>DIV 1</p>
<div className="btn" onClick={ () => setCount(count + 1) } >Next</div>
</div>
} else if (count = 1) {
<div className="box">
<p>DIV 2</p>
<div className="btn" onClick={ () => setCount(count - 1) } >Prev</div>
<div className="btn" onClick={ () => setCount(count + 1) } >Next</div>
</div>
}
return (
<div>
{one of the if-statement elements}
</div>
);
};
export default memo(App);
In order to understand this you need to understand how react works, in general to draw/insert HTML elements you must either return it from your function component, or use render class method in your Class component, putting HTML somewhere else will not render them to the page
As for how to achieve this, there are many ways, the simplest for me would be define a set of components you want to render in an array then render them according to the current count number
const App = () => {
const [count, setCount] = React.useState(0);
const components = [
<div>1</div>,
<div>2</div>,
<div>3</div>,
<div>4</div>
]
return <div>
{
// render component from our components array
components[count]
}
{/* show previous button if we are not on first element */}
{count > 0 && <button onClick={() => setCount(count - 1)}>prev</button>}
{/* hide next button if we are at the last element */}
{count < components.length - 1 && <button onClick={() => setCount(count + 1)}>next</button>}
</div>
}

Remove current index from array in React onClick

I'm fairly new to React. I have built a component that adds an input field onClick. What I need to do is add functionality to a button underneath each new input that deletes that specific input. For example, if 3 inputs are created 1,2,3 and input 2 is deleted 1 and 3 remain.
My code contains a function named onRemoveChild() that has some commented out code of my initial attempt at solving the problem using closest(). The issue with this is that state isn't correctly updated so after an input is removed the field label numbers are out of sync.
Thanks in advance and let me know if more explanation is needed.
import React from 'react'
import {
Button,
TextField,
Typography,
} from '#material-ui/core'
class TextFieldAddNew extends React.Component {
state = {
numChildren: 0
}
render () {
const children = [];
for (var i = 0; i < this.state.numChildren; i += 1) {
children.push(<ChildComponent key={i} number={i+2} removeChild={this.onRemoveChild} />);
};
return (
<ParentComponent addChild={this.onAddChild} theCount={this.state.numChildren}>
{children}
</ParentComponent>
);
}
onAddChild = () => {
this.setState({
numChildren: this.state.numChildren + 1
});
}
onRemoveChild = () => {
document.getElementById('removeInput').closest('div').remove();
}
}
const ParentComponent = (props) => (
<div className="card calculator">
<div id="childInputs">
{props.children}
</div>
{
props.theCount >= 4 ? (
<div className="warning">
We recommend adding no more that 5 opt-in's
</div>
) : ''
}
<Button
className="addInput"
onClick={props.addChild}>
+ Add another option
</Button>
</div>
);
const ChildComponent = (props) => (
<>
<TextField
id={'opt-in-' + props.number}
label={'Opt-in ' + props.number}
name={'opt-in'}
variant="outlined"
size="small"
margin="normal"
color="secondary"
className="halfInput"
/>
<Typography id="removeInput" className="removeInput"
onClick={props.removeChild}>
- Remove option
</Typography>
</>
);
export default TextFieldAddNew;
You can pass the index as part of calling removeChild like below:
children.push(<ChildComponent key={i} number={i+2} removeChild={()=>{this.onRemoveChild(i)}}
Also instead of keeping the numChildren in the state, you should keep the children. This way it would be easy to remove and add nodes to it. Then you can easily do:
children.splice(i,1) and then update the state, this way auto render will update the dom for you.

How to use single button for two purpose

I made a component for one Button with animation effect so that i can use it in my other components by simply importing it. I did a lot of code in Button component for animation so i don't want to use a lot of same code again and again in other components.
Is there any way I can use the Button for different events by just importing. For example; In one component I import the Button as submitting the user information and in other component i am import the Button for displaying user data.
Of course! This is the components idea. You mught want to receive a prop in your Button to handle whatever happens onClick.
I.E.
Parent Component
create the especific function to handle
function handleClick() { ... }
<YourCustomButton onClick={handleClick} />
In YourCustomButton just use this function on event
class YourCustomButton extends React......
<Button onClick={this.props.onClick}> ...
Create a props for button to provide an option like
Implement in other component:
<custombutton mode={"submitmode"} clickevent={handleBtnClick} />
<custombutton mode={"displaymode"} clickevent={handleBtnClick} />
handleBtnClick=() =>{
if(mode =="submitmode"){
//..do code for submit
}
else if(mode=="displaymode"){
//..do code for display
}
}
Actual button component:
class Button extends custombutton{
handleClick =() =>{
this.props.clickevent();
}
render(){
return(
{props.mode=="submitmode" &&
<button type="submit" onClick={handleClick} class="submitstyle" />
}
{props.mode=="displaymode" &&
<button class="displaystyle" onClick={handleClick} />
}
);
}
}
Of course there is, just give it different props:
Example on codesandbox:
https://codesandbox.io/s/admiring-wing-okiqw
The parent:
function App() {
return (
<div className="App">
<h1>Hi there</h1>
<h2>Click on first button to sum 1, click to second button to sum 2!</h2>
<Fbutton functionality="sumone" />
<Fbutton functionality="sumtwo" />
</div>
);
}
You are calling Fbutton two times, one with sumone another with sumtwo props.
The son:
function Fbutton(props) {
const [count, setCount] = useState(0);
const sumone = () => setCount(count + 1);
const sumtwo = () => setCount(count + 2);
return (
<div>
<button onClick={props.functionality === "sumone" ? sumone : sumtwo}>
Sum: {count}
</button>
</div>
);
}
suppose you have a button like <Button pageName="home" />
And in that component suppose ,
class Button extends Compoenent {
onClick =()=>{
if(this.props.pageName == "home"){
// do your home action
} else if(this.props.pageName == "tab"){
// do your tab action
}
}
render(){
return(
<Button onClick={() => this.onClick()} />
)
}
}
Hope thats clear, feel free for doubts

how to go about with this Short circuit evaluation?

I want to add PictureAlbums along with iframe. Basically, I'm trying to write like this: if timerStates is complete then render <iframe> and also PictureAlbum component.
With the code below I'm able to render only <PictureAlbums /> and not iframe. How do I solve this?
import React from 'react';
import PictureAlbums from './PictureAlbums';
import * as timerStates from './TimerState';
const lp = (val) => {
if (val < 10) return `0${val}`;
return `${val}`;
}
const TimerDisplay = (props) => (
<div>
<div>
{
(props.timerState === timerStates.COMPLETE)
&& <iframe src="https://www.youtube.com/"></iframe>
&& <PictureAlbums />
}
</div>
<div>
<h2>{`${lp(props.currentTime.get('hours'))}:${lp(props.currentTime.get('minutes'))}:${lp(props.currentTime.get('seconds'))}`}</h2>
</div>
</div>
);
You can use a React.Fragment for that to give both elements a common parent without inserting an extra element in the DOM.
{props.timerState === timerStates.COMPLETE && (
<React.Fragment>
<iframe src="https://www.youtube.com/"></iframe>
<PictureAlbums />
</React.Fragment>
)}

Resources