I have this carousel component that I'm calling from a page but it's not showing until I refresh the page. If I have the Box component and switch to the Carousel component it doesn't work until I refresh the page but if I render the Carousel component first, render Box then go back to Carousel it works. Is this a bug with ReactDOM or what?
My carousel component:
const Carousel = () => {
const { carousel, setCarousel } = useContext(AppContext);
useEffect(() => {
setCarousel(true);
return () => {
setCarousel(false);
};
}, [carousel]);
const items = ["image1.svg", "image2.svg", "image1.svg", "image2.svg"];
const responsive = {
0: { items: 1 },
1024: { items: 2 },
};
return (
<div
className={`container flex`}
style={{
marginTop: "40px",
}}>
<AliceCarousel
disableDotsControls
disableButtonsControls
autoHeight={true}
autoWidth={true}
responsive={responsive}
animationType='slide'>
{items.map((i, index) => (
<img src={i} key={index} alt='' srcSet='' className={Styles.slider} />
))}
</AliceCarousel>
</div>
);
};
And I'm switching/calling it on another component like this:
const { carousel, modalIsOpen, openModal, closeModal } = useContext(
AppContext
);
return (
<div className={carousel ? Styles.layout : ""}>
<div>
<Box />
</div>
</div>
)
I need to make the component re-render when it's called or something so that it works properly, even when I call the AliceCarousel component on my page directly I still have this issue.
Is this something to do with React or the component itself? Thanks
Your useEffect logic leads to infinity loop as after changing the state to true, by having the state in dep array [carousel], you changing it back to false from the cleanup function.
// Useless useEffect, always false.
useEffect(() => {
setCarousel(true);
return () => {
setCarousel(false);
};
}, [carousel]);
See When does the useEffect's callback's return statement execute?
Related
CodeSandbox - https://codesandbox.io/s/distracted-taussig-n7e7q3?file=/src/App.js
As you can see, I iterate over the flaggers array with .map and render <div>true</div> or <div onClick={() => setToTrue(flag)}>false</div>. I assumed that if I were to click the second div, the refer property of that flag would be set to true and the component would re-render, making the div change to <div>true</div> but that doesn't seem to be the case.
In the setToTrue function I console.log the post object, and I can see that the refer property of the second flag has changed to true, but it is not shown in the UI.
import "./styles.css";
export default function App() {
const post = {
flaggers: [
{
refer: false
},
{
refer: false
}
]
}
const setToTrue = (flag) => {
flag.refer = true;
console.log(post)
}
return (
<div className="App">
{post.flaggers.map((flag) => (
<div>
{flag.refer ? <div>true</div> : <div onClick={() => setToTrue(flag)}>false</div>}
</div>
))}
</div>
);
}
Well, that's not how react you have to setState value to trigger the component to rerender which will cause to UI change try the below code it will work fine as I set a value on onClick that causes the component to rerender in short. I would suggest reading the documentation before going into the coding and know-how react works
React Documentation
export default function App() {
const [post, setPost] = React.useState([
{
refer: false,
},
{
refer: false,
},
]);
const setToTrue = (boolFlag, index) => {
const tempPost = [...post];
tempPost[index].refer = boolFlag;
setPost(tempPost);
};
return (
<div className='App'>
{post.map((flag, index) => (
<div key={`${index}flag`}>
{flag.refer ? <div onClick={() => setToTrue(false, index)}>true</div> : <div onClick={() => setToTrue(true, index)}>false</div>}
</div>
))}
</div>
);
}
I want my Home component to load first and then everything else? How can I achieve this?
Or can I load resources in the background while I'm showing a spinner and then show the whole page? Something like this..
useEffect(() => {
setTimeout(() => {
setIsLoading(false);
}, 2500);
}, []);
return isLoading ? (
<Spinner />
) : (
<div className="App">WholePage</div>
What I would do is use the useEffect hook for the priority component that you want to load first and set a state in there to load the remaining components.
My code would look something like this:
function App(){
const [homeLoaded,setHomeLoaded] = useState(false)
const getHomeLoaded = (homeLoaded) => {
setHomeLoaded(homeLoaded)
}
return(
<>
<Home gethomeLoaded = {getHomeLoaded}/>
{homeLoaded ? <OtherComponents>:null}
</>
)
}
The home component would look something like this:
function Home(){
useEffect(() => {
props.getHomeLoaded(true)
}, []);
return(
<div>Your home content goes here</div>
)
}
What I am essentially doing is passing a state to the parent component from Home so that I can set homeLoaded to true only after Home component is loaded
I am trying to use setTimeout to flip a useState between true and false every 6 seconds. I use this state to add/ remove a class from a div - this will cause the div to toggle between top: 0 to top: 100% (and a transition takes care of the animation).
To be more specific, I have an iPhone wrapper image with a content image inside it. The idea is that it will slowly scroll to the bottom of the content image, and then after 6 seconds (from the time it started scrolling down), it will then start scrolling back up, ad infinitum. However, it's not working right at all.
I've tested it with an onClick and it works exactly as I intend. However, with the setTimeout logic:
The state is always false - even if we set it to true and then log it
The state is always true from the perspective of the JSX - the class is always added, which implies that the state is true
Of course, it cannot be true and false, and it should be flipping its value. Could someone tell me why it's not working and perhaps tell me why it's acting in this bizarre way?
import React, { useState } from 'react';
import iphone from '../Images/iphone.png'
const Iphone = (props) => {
const [isInitialised, setInitialised] = useState(false)
const [animating, setAnimating] = useState(false)
const startAnimation = () => {
setAnimating(!animating); /* Even if I use `true`, it will log to the console as `false` */
console.warn('animation change iphone!');
console.warn(animating);
console.warn(isInitialised); /* This also always logs as `false` */
setTimeout(() => {
startAnimation();
}, 6000);
}
if (!isInitialised) {
setInitialised(true);
startAnimation();
}
return (
<div className={`iphone align-mobile-center ${animating ? "iphone--animating" : ""}`} onClick={() => setAnimating(!animating)}>
<img className="iphone__image" src={iphone} alt="An iPhone" />
<div className="iphone__content">
<img className="iphone__content-image" src={props.image} alt={props.alt} />
</div>
</div>
)
}
export default Iphone;
I use the isInitialised otherwise it seems to hit an infinite loop.
You can do something like this.
useEffect with empty array as deps so it will only once, you don't need isInitialized state.
In useEffect use setInterval so it will run every 6 seconds.
use callback way to setting state so you always get the correct value of animating.
import React, { useState } from 'react';
import iphone from '../Images/iphone.png'
const Iphone = (props) => {
const [animating, setAnimating] = useState(false)
useEffect(() => {
const startAnimation = () => {
setAnimating(v => !v);
}
const interval = setInterval(() => {
startAnimation();
}, 6000);
return () => clearInterval(interval);
}, []);
return (
<div className={`iphone align-mobile-center ${animating ? "iphone--animating" : ""}`} onClick={() => setAnimating(!animating)}>
<img className="iphone__image" src={iphone} alt="An iPhone" />
<div className="iphone__content">
<img className="iphone__content-image" src={props.image} alt={props.alt} />
</div>
</div>
)
}
export default Iphone;
you can use useEffect and bring setTimeout outside.
import React, { useState } from 'react';
import iphone from '../Images/iphone.png'
const Iphone = (props) => {
const [isInitialised, setInitialised] = useState(false)
const [animating, setAnimating] = useState(false)
useEffect(()=>{
startAnimation();
},[]);
const startAnimation = () => {
setAnimating(!animating);
console.warn('animation change iphone!');
console.warn(animating);
console.warn(isInitialised);
}
setTimeout(() => {
if (!isInitialised) {
setInitialised(true);
startAnimation();
}
}, 6000);
return (
<div className={`iphone align-mobile-center ${animating ? "iphone--animating" : ""}`} onClick={() => setAnimating(!animating)}>
<img className="iphone__image" src={iphone} alt="An iPhone" />
<div className="iphone__content">
<img className="iphone__content-image" src={props.image} alt={props.alt} />
</div>
</div>
)
}
export default Iphone;
I want to render for drag only once, but renders the infinite loops.
i'm use The react Dnd method for this project
this warning is Show : Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
chichihandler = (id) => {
console.log('inApp', id);
this.setState({
hoverdID: 123
})
console.log("hoverd", this.state.hoverdID)
}
render() {
return (
<div className="all">
<Header />
<div className='Products_list' >
{this.state.productData.map((item) => (
<Products key={item.id} item={item} handleDrop={(productId) => this.addItem(productId)} />
))}
</div>
<div className='Store_list' >
<div className="storeName" >Store Name</div>
{this.state.storeData.map((itemS) => (
<Store key={itemS.code} itemS={itemS} chichi={(id) => this.chichihandler(id)} />
))}
</div>
</div>
)
}
storeData Code:
import React, { Component } from 'react'
import { DropTarget } from 'react-dnd'
function collect(connect, monitor) {
return {
connectDropTarget: connect.dropTarget(),
hovered: monitor.isOver(),
item: monitor.getItem()
}
}
class Store extends Component {
render() {
const { connectDropTarget, hovered, itemS } = this.props
const backcgroundColor = hovered ? 'lightgreen' : ''
if (hovered) {
this.props.chichi(itemS.name)
console.log(itemS.name)
}
return connectDropTarget(
<div>
<div id={itemS.code} className='Store' style={{ background: backcgroundColor }}>
{this.props.itemS.name}
</div>
</div>
)
}
}
export default DropTarget('item', {}, collect)(Store)
The loop occurs in the render method of your Store component, where it
calls this.props.chici(itemS.name), which
calls your chichiHandler() function, which
calls this.setState() on the parent component, which
triggers a re-render, which
causes Store to re-render, which...
It looks like you want the chichi function to be called when the user hovers over something, in which case you're better off using the onMouseOver prop on the element in question, rather than try to do it with props (see https://reactjs.org/docs/events.html#mouse-events for more info).
In general you should never call setState() from with in a render(), because it tends to cause these sorts of loops.
import React, { Component } from 'react';
import axios from 'axios';
import { Button, Card } from 'semantic-ui-react';
class Games extends Component {
state = { games:[], showGames: false }
componentDidMount() {
axios.get('/api/board_games')
.then(res => {
this.setState({games: res.data});
})
}
toggleGames = () => {
this.setState({ showGames: !this.state.showGames })
}
gamesList = () => {
const {games} = this.state
return games.map( game =>
<Card key={game.id}>
<Card.Content>
<Card.Header>{game.title}</Card.Header>
<Card.Description>Players: {game.min_players} - {game.max_players}</Card.Description>
<Card.Description>Company: {game.company}</Card.Description>
<Card.Description>Time Needed: {game.time_needed}</Card.Description>
</Card.Content>
<Card.Content extra>
<Button basic color='green'>
Add to Library
</Button>
</Card.Content>
</Card>
)
}
render() {
const showGames = this.state
return (
<div>
<h1>Games</h1>
<h3>Your Games</h3>
{ showGames ? (
<div>
<Card.Group itemsPerRow={4}>{this.gamesList()}</Card.Group>
</div>
)
: (
<button onClick={this.toggleGames()}>Add a Game</button>
)
}
</div>
)
}
}
export default Games;
In my mind, the render return should be checking if showGames is true or false. It's defaulted to false in the state at the beginning. For that reason, it should render the "add a game" button. But if you click that button, it should toggle the showGames to true and render the game cards. Instead, it automatically renders the cards when I arrive on the page. I would also like to add Done Adding to the first part of the if/else, but when I do that I get " Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops."
the way you're setting the onClick event is causing it to be constantly called. you should format it either like this:
onClick={this.toggleGames}
or like this:
onClick={() => this.toggleGames()}
Just edit the following line in your render() :
const showGames = this.state.showGames;
or
const { showGames } = this.state;
At the moment your showGames constant is an object instead of a boolean. Hence you are not able to use it conditionally.
Hope this helps!
you have set wrong onClick action from the button
// the onClick is executing in every render.
<button onClick={this.toggleGames()}>Add a Game</button>
// right way
button onClick={() => this.toggleGames()}>Add a Game</button>