setTimeout runs indefinetly inside useEffect - javascript

I am trying to make a notification toast component. And I want it to be removed after 2 seconds (not-shown on the screed) Although, it is removed (not-shown on the screen via top:-100 argument), the component is getting rendered infinitely. You can see it from the console.log's I have placed inside the component and inside the useEffect call with setTimeout.
My expectation is that setTimeout should run setShowState after 2 seconds and then useEffect should do the cleanup and remove the timer. So everything is back to normal until showState changes.
import React, {useEffect, useState} from 'react'
import I18n from '../../i18n'
import styled from 'styled-components'
import {createGlobalStyle} from 'styled-components'
import {useSelector} from 'react-redux'
const NotificationStyle = createGlobalStyle`
#media (max-width: 500px) {
.notification_mssg {
left: 10px;
}
}
`
const Container = styled.div`
color: white;
position: fixed;
top: ${(props) => props.top}px;
right: 16px;
z-index: 2000;
transition: top 0.5s ease;
`
const NoticitactionIcon = styled.div`
float: left;
font-size: 27px;
width: 40px;
height: 40px;
text-align: center;
`
const NotificationMessage = styled.span`
padding: 10px;
line-height: 40px;
`
function NotificationAlertRoot(props) {
const create_notification = useSelector((state) => state.notifications.create_notification)
const {message, code} = create_notification.success_info
const [showState, setShowState] = useState({top: -100, msg: message, bgColor: '#444'})
// show notification
useEffect(() => {
setShowState({top: 96, msg: I18n.t(message), bgColor: backgroundColor(code)})
}, [message, code])
console.log('amIrendered', showState) // although showState doesn't change, component is getting rendered infinitely :/
// hide notification after 2 seconds
useEffect(() => {
const timerId = setTimeout(() => {
setShowState({
top: -100,
msg: '',
bgColor: `#ffffff00`,
})
console.log("timerId", timerId) // I see timerId is changing so the problem most probably in this useEffect call.
}, 2000)
return () => {
clearTimeout(timerId)
}
}, [showState])
const notificationIcon = (bgColor) => {
switch (bgColor) {
case '#32c786':
return (
<NoticitactionIcon style={{background: '#2aa872'}}>
<i className="zmdi zmdi-info" />
</NoticitactionIcon>
)
case '#ffc721':
return (
<NoticitactionIcon style={{background: '#fabb00'}}>
<i className="zmdi zmdi-alert-triangle" />
</NoticitactionIcon>
)
case '#ff6b68':
return (
<NoticitactionIcon style={{background: '#ff4642'}}>
<i className="zmdi zmdi-alert-circle" />
</NoticitactionIcon>
)
default:
return <span></span>
}
}
function backgroundColor(code) {
switch (Math.floor(code / 100)) {
case 2:
return '#32c786'
case 3:
return '#ffc721'
case 4:
return '#ff6b68'
case 5:
return '#ff6b68'
default:
return '#444'
}
}
return (
<React.Fragment>
<NotificationStyle />
<Container
className="notification_mssg"
top={showState.top}
style={{background: showState.bgColor}}
>
{notificationIcon(showState.bgColor)}
<NotificationMessage>{showState.msg}</NotificationMessage>
</Container>
</React.Fragment>
)
}
export default NotificationAlertRoot
Do you have an idea what is wrong above?

I guess the problem comes from your dependency array. Your useEffect is dependent on showState and each time, you are calling setShowState in your useEffect when setShowState is called showState changes and then again, your useEffect
gets invoked(it is dependent on ShowState), and again setShowState is called and ...
infinity loop!

I found the root of the problem. Sometimes you are too focused on something and you forget the little details of useEffect. It is always dangerous to provide objects as dependency arrays to useEffect. The dependency array values should be simple values. So now I introduced a new state (flag, setFlag) with just boolean values and I make the second useEffect just to follow that simple value. Everything is working just fine now.
import React, {useEffect, useState} from 'react'
import I18n from '../../i18n'
import styled from 'styled-components'
import {createGlobalStyle} from 'styled-components'
import {useSelector} from 'react-redux'
const NotificationStyle = createGlobalStyle`
#media (max-width: 500px) {
.notification_mssg {
left: 10px;
}
}
`
const Container = styled.div`
color: white;
position: fixed;
top: ${(props) => props.top}px;
right: 16px;
z-index: 2000;
transition: top 0.5s ease;
`
const NotificationIcon = styled.div`
float: left;
font-size: 27px;
width: 40px;
height: 40px;
text-align: center;
`
const NotificationMessage = styled.span`
padding: 10px;
line-height: 40px;
`
function NotificationAlertRoot(props) {
const create_notification = useSelector((state) => state.notifications.create_notification)
const {message, code} = create_notification.success_info
const [showState, setShowState] = useState({top: -100, msg: message, bgColor: '#444'})
const [flag, setFlag] = useState(false) // when you follow the showState at the second useEffect you have an infinite loop. Because it is an object.
// show notification
useEffect(() => {
setShowState({top: 96, msg: I18n.t(message), bgColor: backgroundColor(code)})
setFlag(true)
}, [message, code])
// hide notification after 2 seconds
useEffect(() => {
const timerId = setTimeout(() => {
setShowState({top: -100,msg: '', bgColor: `#ffffff00`})
setFlag(false)
}, 2000)
return () => {
clearTimeout(timerId)
}
}, [flag]) // showState
const notificationIcon = (bgColor) => {
switch (bgColor) {
case '#32c786':
return (
<NotificationIcon style={{background: '#2aa872'}}>
<i className="zmdi zmdi-info" />
</NotificationIcon>
)
case '#ffc721':
return (
<NotificationIcon style={{background: '#fabb00'}}>
<i className="zmdi zmdi-alert-triangle" />
</NotificationIcon>
)
case '#ff6b68':
return (
<NotificationIcon style={{background: '#ff4642'}}>
<i className="zmdi zmdi-alert-circle" />
</NotificationIcon>
)
default:
return <span></span>
}
}
const backgroundColor = (code) => {
switch (Math.floor(code / 100)) {
case 2:
return '#32c786'
case 3:
return '#ffc721'
case 4:
return '#ff6b68'
case 5:
return '#ff6b68'
default:
return '#444'
}
}
return (
<React.Fragment>
<NotificationStyle />
<Container
className="notification_mssg"
top={showState.top}
style={{background: showState.bgColor}}
>
{notificationIcon(showState.bgColor)}
<NotificationMessage>{showState.msg}</NotificationMessage>
</Container>
</React.Fragment>
)
}
export default NotificationAlertRoot

Related

React.js/Next.js Loader is showing in the wrong place on the page

I want to show a Loader on top of everything while fetching data from my API. I asked this question and implemented the answer, and it is working, but it shows it not on top level, but inside the page itself. When looking at the html tree I can see that it is on the top level.
This is what happens when btnAll is clicked:
After scrolling down:
This is the HTML tree:
Why does it put it inside the page?
loading.provider.js
import { createContext, useContext, useState } from "react";
const LoadingContext = createContext({
loading: false,
setLoading: null
});
export function LoadingProvider({ children }) {
const [loading, setLoading] = useState(false);
const value = { loading, setLoading };
console.log(`LoadingProvider: ${loading}`);
return (
<LoadingContext.Provider value={value}>{children}</LoadingContext.Provider>
);
};
export function useLoading() {
const context = useContext(LoadingContext);
console.log(`Using LoadingContextProvider`);
if (!context) {
throw new Error('useLoading must be used within LoadingProvider');
}
return context;
};
app.js
export default function App({ Component, pageProps }) {
return (
<>
<CssBaseline />
<AppStateProvider>
<LoadingProvider>
<Loader />
<Layout>
<Component {...pageProps} />
</Layout>
</LoadingProvider>
</AppStateProvider>
</>
);
};
Loader.js
import { useLoading } from "#/Providers/loading.provider";
import { useEffect } from "react";
import LoadingScreen from "./LoadingScreen";
const Loader = () => {
const { loading } = useLoading();
useEffect(() => {
console.log(`[app.js/#useEffect]: useLoading() value changed to: ${loading}`);
}, [loading]);
return loading && <LoadingScreen loading={true} bgColor='#fff' spinnerColor={'#00A1FF'} textColor='#676767'></LoadingScreen>;
};
export default Loader;
LoadingScreen.js
import styles from './LoadingScreen.module.css';
export default function LoadingScreen() {
return (
<div className={styles.loading}>
<div className={styles.dot}></div>
<div className={styles.dot}></div>
<div className={styles.dot}></div>
<div className={styles.dot}></div>
<div className={styles.dot}></div>
</div>
);
}
This is the css part of style.loading inside LoadingScreen.module.css:
.loading {
display: flex;
justify-content: center;
align-items: center;
font-size: 2rem;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 99;
background-color: #FFFFFF;
}
I am calling it inside [identifier].js page like that:
import { useLoading } from "#/Providers/loading.provider";
export default function MainPage({ response }) {
const { loading, setLoading } = useLoading();
const btnClickedAll = async (data) => {
setLoading(true);
};
return (
<Fragment>
<Button sx={{ ml: 2 }} key={'btnAll'} onClick={btnClickedAll} variant="contained">All</Button>,
</Fragment>
);
}
Updated:
Try set the style .loading position: fixed;
Old:
Try moving down the <Loader /> in app.js
...
<LoadingProvider>
<Layout>
<Component {...pageProps} />
</Layout>
<Loader />
</LoadingProvider>
...
Or it's better to put Loader in loading.provider.js and remove <Loader /> from app.js
...
<LoadingContext.Provider value={value}>
{children}
<Loader />
</LoadingContext.Provider>
...

React: adding ternary operator for component breaks progress bar

I am still learning React and am trying to parse text from an API call into json objects while displaying progress in a progress bar.
Below, Home.js uses useEffect hook to
call getData that grabs (lots of) API text data
call parseText to parse returned text
call parseSeriesId to parse returned results
set seriesIdParts
Finally, seriesIdParts is passed to Occupation.js as props. That works fine and working code is displayed below.
My problem is that I want to make the progress bar disappear when parseText is finished. I found a post that did it using a ternary operator, so trying that I changed this:
<ProgressBar percentage={percentage}/>
to this:
{percentage >= 100 ? console.log("Done.") : <ProgressBar percentage={percentage}/>
and now the progressBar disappears, but it no longer populates green. I can log the percentage value just fine in both ProgressBar.js and Filler.js, so it seems to be that the Filler width element is not picking up the percentage value increment:
<div className="filler" style={{ width: `${percentage}%`}}>
How do I fix this?
App.js
import React from 'react';
import './App.css';
import Home from './components/Home';
function App() {
return (
<div className="App">
<Home />
</div>
);
}
export default App;
Home.js
import React from 'react';
import { useState, useEffect } from 'react';
import Occupation from './Occupation';
import ProgressBar from './ProgressBar';
const Home = () => {
const [seriesIdParts, setSeriesIdParts] = useState([]);
const [percentage, setPercentage] = useState(0);
async function getData(url) {
const response = await fetch(url);
if( response.status !== 200 ) {
throw new Error('Problem calling ' + url);
}
return response.text();
}
function parseSeriesId(data) {
let results = new Set()
data.forEach(function(value) {
let areaCode = value.seriesId.substring(4, 11);
let industry = value.seriesId.substring(11, 17);
let occupation = value.seriesId.substring(17, 23);
let dataType = value.seriesId.substring(23, value.seriesId.length);
let seriesIdVal = value.value;
if( dataType === '04') {
let result = {areaCode: areaCode, industry: industry, occupation: occupation, dataType: dataType, seriesIdVal: seriesIdVal};
results.add(result);
}
})
return results;
}
useEffect(() => {
getData('https://download.bls.gov/pub/time.series/oe/oe.data.0.Current')
.then(data => {
const parseText = function(data) {
data = data.split('\n');
let localNum = 1090;
let results = new Set();
let intervalAmount = 100 / localNum;
for( let i=1; i<=localNum; i++ ) {
setPercentage((i * intervalAmount));
let line = data[i];
if( line !== "") {
line = line.split('\t');
let seriesIdValue = line[0].trim();
let valueValue = line[3].trim();
if (valueValue !== '-') {
results.add({'seriesId': seriesIdValue, 'value': valueValue});
}
}
}
return results;
}
return parseText(data, 100);
})
.then(data => {
return parseSeriesId(data);
})
.then(data => {
setSeriesIdParts(data);
})
.catch(err => console.log(err));
}, [percentage]);
return(
<div className="homeComponent">
<label id="parsetext" >
Parsing Text...
<ProgressBar percentage={percentage}/>
</label>
{seriesIdParts && <Occupation seriesIdParts={seriesIdParts}/>}
</div>
);
}
export default Home;
ProgressBar.js
import React, { useState } from 'react';
import Filler from './Filler';
const ProgressBar = ({percentage}) => {
// console.log('Pbar: ', percentage); <== Displays fine!
return (
<div className="progressbar">
<Filler percentage={percentage}/>
</div>
);
}
export default ProgressBar;
Filler.js
import React from 'react';
const Filler = ({percentage}) => {
return (
<div className="filler" style={{ width: `${percentage}%`}}>
{console.log('filler: ' + percentage)}. <== Displays fine!
</div>
);
}
export default Filler;
Occupation.js
import React from 'react';
import { useState, useEffect } from 'react';
const Occupation = ({seriesIdParts}) => {
console.log('seriesIdParts in Occupation:', seriesIdParts.size) <== Displays fine!
//other code...
};
export default Occupation;
App.css
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
#media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-spin infinite 20s linear;
}
}
.App-header {
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
.App-link {
color: #61dafb;
}
#keyframes App-logo-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.progressbar {
margin: 4em auto;
width: 80%;
height: 2em;
background-color: lightgray;
border-radius: 30px;
}
.filler {
height: inherit;
width: 0px;
background-color: green;
border-radius: inherit;
}
#parsetext {
text-align: 'center';
margin: 4em 0 0;
}
UPDATE: If there is a better way to make the progress bar disappear, I'm open to suggestions!
What if you set the height of the filler class to like 20px instead of inherit just to try?
.filler {
height: inherit;
width: 0px;
background-color: green;
border-radius: inherit;
}
I tried to test just that div and I couldn't see it with "inherit"

framer.motion animation is instant instead of animating

I have several boxes that I want to animate through,
Here's a simple app example (Also a codesandbox here)
Each "box" should fade in and fade out, however, in this example, the animation happens isntantly.
const Box = styled.div`
width: 100px;
height: 100px;
background: green;
`;
const Test = ({ isActive }) => {
return (
<motion.div
animate={isActive ? { opacity: 1 } : { opacity: 0 }}
transition={{ duration: 3 }}
>
<Box>hello world</Box>
</motion.div>
);
};
export default function App() {
const [currentIndex, setCurrentIndex] = useState(0);
const boxes = [
{
component: ({ isActive }) => <Test isActive={isActive} />
},
{
component: ({ isActive }) => <Test isActive={isActive} />
}
];
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<div onClick={() => setCurrentIndex(currentIndex === 0 ? 1 : 0)}>
{boxes.map((box, index) => {
const isActive = index === currentIndex;
return <box.component isActive={isActive} />;
})}
</div>
</div>
);
I have never used framer.motion before, but looking at their documentation, I think you can use variants, to achieve what you need. https://www.framer.com/api/motion/examples/
I've slightly refactored your code, to get it working:
import "./styles.css";
import { motion } from "framer-motion";
import styled from "styled-components";
import { useEffect, useState } from "react";
const Box = styled.div`
width: 100px;
height: 100px;
background: green;
`;
const variants = {
open: { opacity: 1 },
closed: { opacity: 0 }
};
const Test = ({ index, currentIndex }) => {
return (
<motion.div
animate={index === currentIndex ? "open" : "closed"}
variants={variants}
transition={{ duration: 3 }}
>
<Box>hello world</Box>
</motion.div>
);
};
export default function App() {
const [currentIndex, setCurrentIndex] = useState(0);
const boxes = ["a", "b"];
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>{currentIndex}</h2>
<div onClick={() => setCurrentIndex(currentIndex === 0 ? 1 : 0)}>
{boxes.map((box, i) => {
return <Test index={i} currentIndex={currentIndex} />;
})}
</div>
</div>
);
}
the currentIndex is passed as props to the child Test components, they check themselves whether their index matches the currentIndex and update their animations accordingly.
Edited codesandbox here: https://codesandbox.io/s/suspicious-austin-tymvx
In framer motion, you have useCycle properties. Here is an example.
Code in example:
import * as React from "react";
import { render } from "react-dom";
import { Frame, useCycle } from "framer";
import "./styles.css";
export function MyComponent() {
const [animate, cycle] = useCycle(
{ scale: 1.5, rotate: 0, opacity: 1 },
{ scale: 1.0, rotate: 90, opacity: 0 }
);
return (
<Frame
animate={animate}
onTap={() => cycle()}
size={150}
radius={30}
background={"#fff"}
/>
);
}
const rootElement = document.getElementById("root");
render(<MyComponent />, rootElement);
and some simple css:
body {
margin: 0;
padding: 0;
}
#root {
font-family: sans-serif;
text-align: center;
width: 100vw;
height: 100vh;
display: flex;
place-content: center;
place-items: center;
background: rgba(0, 85, 255, 1);
margin: 0;
padding: 0;
perspective: 1000px;
}
I don't recommend you to use this type of construction: animate={index === currentIndex ? "open" : "closed"} , because you might have some lagging/breaking animation.
Try always to search examples/elements of MotionAPI lib. You will have less code lines and mostly "clean" code with no useless variables.

React: can I change a state (useState) without causing a repaint, so that I can see a css transition?

I have different "cards" that on click onClick I want their margin-left property to be modified
To do that I use useState, for which I have only one state that is an object that stores the states for all the cards
The below example code shows the problem, but a simplified version that doesn't have a component <Type> and that uses a simple elements array works as expected
So, if I need to use a structure like the one below, how could I keep the transition effect?
Example code
https://codesandbox.io/s/keen-shadow-2v16s?fontsize=14&hidenavigation=1&theme=dark
import React, { useState } from "react";
import styled from "#emotion/styled";
export default function App() {
const [userTap, setUserTap] = useState({});
const elements1 = [...Array(5)];
const elements2 = [...Array(3)];
const Type = ({ list }) =>
list.map((el, i) => (
<Ingredient
key={"draggable" + i}
onClick={e => {
e.stopPropagation();
e.preventDefault();
userTap[i] = userTap[i] ? 0 : 1;
setUserTap({ ...userTap }); // create a new ref to provoke the rerender
return;
}}
userTap={userTap[i]}
>
<div>item</div>
</Ingredient>
));
return (
<>
<Type list={elements1} />
<Type list={elements2} />
</>
);
}
const Ingredient = styled.li`
list-style: none;
cursor: pointer;
margin: 5px;
padding: 5px;
background: #ccc;
border-radius: 3px;
width: 50px;
margin-left: ${props => (props.userTap ? "100px" : "15px")};
transition: all 0.2s ease-in;
`;
The only thing needed to be done, as #larz suggested in the comments, is to move the useState to the last component, as shown below
https://codesandbox.io/s/affectionate-hawking-5p81d?fontsize=14&hidenavigation=1&theme=dark
import React, { useState } from "react";
import styled from "#emotion/styled";
export default function App() {
const elements1 = [...Array(5)];
const elements2 = [...Array(3)];
const Type = ({ list, type }) => {
const [userTap, setUserTap] = useState({});
return list.map((el, i) => (
<Ingredient
key={"draggable" + i}
onClick={e => {
e.stopPropagation();
e.preventDefault();
userTap[type + i] = userTap[type + i] ? 0 : 1;
setUserTap({ ...userTap }); // create a new ref to provoke the rerender
return;
}}
userTap={userTap[type + i]}
>
<div>item</div>
</Ingredient>
));
};
return (
<>
<Type list={elements1} type="one" />
<Type list={elements2} type="two" />
</>
);
}
const Ingredient = styled.li`
list-style: none;
cursor: pointer;
margin: 5px;
padding: 5px;
background: #ccc;
border-radius: 3px;
width: 50px;
margin-left: ${props => (props.userTap ? "100px" : "15px")};
transition: all 0.2s ease-in;
`;

States aren't being updated

I'm currently working on an image uploader component in React. Everything works fine but the deleting method. I've read a couple of articles on how to update arrays/objects and the idea of immutable state. Here's what I've tried:
.filter()
.slice()
.splice() (I doubt this would work as it modifies the original array)
And I always got this error no matter what I tried:
Warning: Cannot update during an existing state transition (such as within `render`). Render methods should be a pure function of props and state.
And this is my code:
ImageUploader.js
import React, { Component } from 'react';
import styled from 'styled-components';
import FileUploadButton from '../FileUploadButton';
import ImagePreviewer from './ImagePreviewer';
import {
Typography,
Button
} from '#material-ui/core';
import theme from '../../../theme';
import uuidv5 from 'uuid/v5';
const StyledPreviewerContainer = styled.div`
display: flex;
margin: ${theme.spacing.unit}px 0;
overflow: hidden;
overflow-x: auto;
`;
export default class ImageUploader extends Component {
state = {
uploadedImages: []
}
updateImages = e => {
const { uploadedImages } = this.state,
files = [...e.target.files],
inexistentImages = files.filter(image => uploadedImages.indexOf(image) === -1);
this.setState(prevState => ({
uploadedImages: [...prevState.uploadedImages, ...inexistentImages]
}));
this.props.onChange(e);
}
removeImages = image => {
const { uploadedImages } = this.state,
imageIndex = uploadedImages.indexOf(image);
this.setState(prevState => ({
uploadedImages: prevState.uploadedImages.filter((image, index) => index !== imageIndex)
}));
};
render() {
const {
className,
label,
id,
multiple,
name,
onBlur
} = this.props, {
uploadedImages
} = this.state;
return (
<div className={className}>
<Typography>
{label}
</Typography>
<StyledPreviewerContainer>
{uploadedImages.map(image =>
<ImagePreviewer
src={URL.createObjectURL(image)}
image={image}
removeImages={this.removeImages}
key={uuidv5(image.name, uuidv5.URL)}
/>
)}
</StyledPreviewerContainer>
<FileUploadButton
id={id}
multiple={multiple}
name={name}
onChange={this.updateImages}
onBlur={onBlur}
/>
<Button>
Delete all
</Button>
</div>
);
}
}
ImagePreviewer.js
import React, { Component } from 'react';
import styled from 'styled-components';
import AnimatedImageActions from './AnimatedImageActions';
import { ClickAwayListener } from '#material-ui/core';
import theme from '../../../theme';
const StyledImagePreviewer = styled.div`
height: 128px;
position: relative;
user-select: none;
cursor: pointer;
&:not(:last-child) {
margin-right: ${theme.spacing.unit * 2}px;
}
`;
const StyledImage = styled.img`
height: 100%;
`;
export default class ImagePreviewer extends Component {
state = {
actionsOpened: false
};
openActions = () => {
this.setState({
actionsOpened: true
});
};
closeActions = () => {
this.setState({
actionsOpened: false
});
};
render() {
const {
actionsOpened
} = this.state,
{
src,
image,
removeImages
} = this.props;
return (
<ClickAwayListener onClickAway={this.closeActions}>
<StyledImagePreviewer onClick={this.openActions}>
<StyledImage src={src} />
<AnimatedImageActions
actionsOpened={actionsOpened}
image={image}
removeImages={removeImages}
/>
</StyledImagePreviewer>
</ClickAwayListener>
);
}
}
AnimatedImageActions.js
import React from 'react';
import styled from 'styled-components';
import { Button } from '#material-ui/core';
import { Delete as DeleteIcon } from '#material-ui/icons';
import { fade } from '#material-ui/core/styles/colorManipulator';
import theme from '../../../theme';
import {
Motion,
spring
} from 'react-motion';
const StyledImageActions = styled.div`
position: absolute;
top: 0;
left: 0;
color: ${theme.palette.common.white};
background-color: ${fade(theme.palette.common.black, 0.4)};
width: 100%;
height: 100%;
display: flex;
`;
const StyledImageActionsInner = styled.div`
margin: auto;
`;
const StyledDeleteIcon = styled(DeleteIcon)`
margin-right: ${theme.spacing.unit}px;
`;
const AnimatedImageActions = ({ actionsOpened, removeImages, image }) =>
<Motion
defaultStyle={{
scale: 0
}}
style={{
scale: spring(actionsOpened ? 1 : 0, {
stiffness: 250
})
}}
>
{({ scale }) =>
<StyledImageActions style={{
transform: `scale(${scale})`
}}>
<StyledImageActionsInner>
<Button
color="inherit"
onClick={removeImages(image)}
>
<StyledDeleteIcon />
Delete
</Button>
</StyledImageActionsInner>
</StyledImageActions>
}
</Motion>
;
export default AnimatedImageActions
Any help would be greatly appreciated!
Could it be that onClick={removeImages(image)} should be onClick={()=>removeImages(image)}?
Otherwise, removeImages is calling setState in AnimatedImageActions's render pass.

Categories