Next JS Framer Motion Fade Out - next.js

I'm trying to use framer-motion and Next js to create a fade in out effect but it never fades out. I understand AnimatePresence allows components to animate out when they're removed from the React tree. which is probably my issue but I don't know react well enough to know how to fix my structure. Can anyone recommend a way to get it fading out? Here are some pages...
_app.js
export default class BookingApp extends App {
render() {
return (
<Provider session={pageProps.session}>
<ThemeProvider theme={myTheme}>
<GlobalStyles />
<Layout>
<AnimatePresence exitBeforeEnter>
<Component {...pageProps} key={router.route} />
</AnimatePresence>
</Layout>
</ThemeProvider>
</Provider>
)
}}
Some simple page
class TestPage extends React.Component {
render () {
return <motion.div
exit={{ opacity:0 }}
initial={{ opacity:0 }}
animate={{ opacity:1 }}
>
{resultsList}
</motion.div>;
}}

I just faced similar issue. You motion.div must have a key prop. Check the documentation here. https://www.framer.com/docs/animate-presence/
<motion.div
key={"my_unique_key"}
exit={{ opacity:0 }}
initial={{ opacity:0 }}
animate={{ opacity:1 }}
>
{resultsList}
</motion.div>;

Related

How do I add styles to a component that is passed as prop in React?

I have a component that is passed as a label prop. I need to add width: 100% for it because otherwise, I cannot use justify-content: space between for my div. Here is how it looks like in developer tools.
return (
<div className={classes.row}>
<Checkbox
value={deviceId}
className={classes.checkbox}
checked={Boolean(selected)}
onChange={handleToggle}
label={
<div>
<Tooltip title={t('integrations.deviceEUI')} placement={'top-start'}>
<Typography variant="body1" className={classes.item}>
{deviceEui}
</Typography>
</Tooltip>
<Tooltip title={t('integrations.deviceName')} placement={'top-start'}>
<Typography variant="body1" className={classes.item}>
{name || ''}
</Typography>
</Tooltip>
</div>
}
/>
</div>
);
};
I'm not sure I fully understand the issue, but if you want to simply add styles to a React component, you can simply do the following:
cont labelStyle = {
width: '100%'
};
Then inside your return statement, you could attach this labelStyle to the parent <div> like so:
<div style={labelStyle}>
//other components
</div>
If this isn't what you really mean, then please consider outlining the issue a little more clearly, thanks!
In React Native version of this problem, you can simply give the "style" prop to the component as:
const NewComponent = ({style}) => {}
and now you are able to reach to the "style" prop from another file.
Now, "style" prop is available to use in "NewComponent", write the following code:
<NewComponent style={{}}/>

How to set textTransform to all components globally?

I want to capitalize all text in my muiv5 project. It should be default to capitalize unless stated in sx or styled on the component itself.
What I've tried is:
<ThemeProvider theme={theme}>
<IntlProvider
locale="en"
defaultLocale="en"
messages={AppLocale.en.messages}
>
<CssBaseline />
<GlobalStyles styles={{ root: { textTransform: "capitalize" } }} />
<Component {...pageProps} />
</IntlProvider>
</ThemeProvider>
But it doesn't work. Any best way to do this? Even createTheme is not working for me.
Note: I don't want to use makeStyles. Using Next.js.
You can allow all the children of body to inherit the textTransform value.
<GlobalStyles styles={{ body: { textTransform: "capitalize" } }} />

Framer Motion Page Transition Next JS Router.push

I made the following using the nextjs and framer motion
I have a list of images that I'm mapping over and assigning them a layoutid and an optional variant to animate. The layoutid corresponds to the layoutid on the model1, model2, model3 pages.
Current Behaviour
When first going to the home page and clicking on an image I update some state and set the variant animation to false, this then tells that image to use the layoutid, it then fades out the other images and animates the clicked image into place on the component that is loaded (model1, model2, model3)...Great it works!
If you then click home in the navigation and try clicking an item again it doesn't work, all images are faded out and the clicked image doesn't animated.
Click refresh on the homepage and it works as desired!
here is the code for the page, I suspect it could be something to do with the routing or settings in _app.js
export default function Home() {
const router = useRouter();
const [isClicked, setIsClicked] = useState(null);
const onHandlerClick = (item, href, e) => {
e.preventDefault();
setIsClicked(item);
router.push(href, { scroll: false });
};
return (
<div className="l-grid l-grid-outter">
<div className="c-home-maincontent">
<div>
<main>
<motion.div className="l-grid-3-col" initial="initial" animate="enter" exit="exit" variants={{ exit: { transition: { staggerChildren: 0.1 } } }}>
{images.map((item, index) => {
return (
<motion.div
key={index}
className="c-home-overflowimage c-home-overflowimage2"
layoutId={`imageAnimation${item}`}
variants={isClicked === item ? false : postVariants}
transition={{ ...transition }}
>
<a href={`/model${item}`} onClick={(event) => onHandlerClick(item, `/model${item}`, event)}>
<motion.img
src="/yasmeen.webp"
whileHover={{
scale: 1.1,
}}
/>
</a>
</motion.div>
);
})}
</motion.div>
</main>
</div>
</div>
<Footer />
</div>
);
}
function MyApp({ Component, pageProps }) {
const router = useRouter();
return (
<>
<DefaultSeo {...Seo} />
<AnimateSharedLayout type="crossfade">
<AnimatePresence exitBeforeEnter initial={false}>
<Component {...pageProps} key={router.asPath} />
</AnimatePresence>
</AnimateSharedLayout>
</>
);
}
export default MyApp;
Updated the code to include an animate set to false if its the clicked item
<motion.div className="l-grid-3-col" initial="initial" animate="enter" exit="exit">
{images.map((item, index) => {
return (
<motion.div
key={index}
className="c-home-overflowimage c-home-overflowimage2"
layoutId={`imageAnimation${item}`}
animate={isClicked === item ? false : true}
variants={isClicked === item ? false : postVariants}
transition={{ ...transition }}
>
<a href={`/model${item}`} onClick={(event) => onHandlerClick(item, `/model${item}`, event)}>
<motion.img
src="/yasmeen.webp"
whileHover={{
scale: 1.1,
}}
/>
</a>
</motion.div>
);
})}
</motion.div>

CSS transition doesn't work with react-portal

I am using react-portal to make a dialog and added transition, it went well as
// App.js
return (
<div className="App">
<button onClick={() => setIsOpen(true)}>Click to see dialog</button>
<PortalDialog isOpen={isOpen} setIsOpen={setIsOpen}>
This is content of dialog
</PortalDialog>
{/* animation works */}
</div>
)
//PortalDialog.js
return (
<Portal>
<DialogWrapper className={`${isOpen ? "active" : ""}`}>
{children}
<button onClick={() => setIsOpen(false)}>Close</button>
</DialogWrapper>
</Portal>
);
until I noticed PortalDialog is created every time on render which I don't want so I added a condition
<div className="App">
<button onClick={() => setIsOpen(true)}>Click to see dialog</button>
{isOpen && (
<PortalDialog isOpen={isOpen} setIsOpen={setIsOpen}>
This is content of dialog
</PortalDialog> // animation doesn't work
)}
</div>
Now it's not rendered everytime but CSS transition doesn't work anymore and I think that's because of the condition I added. But I couldn't find a way to add transition with react-portal, what do I need to do here ? I made a codesandbox example here.
Looks like ReactTransitionGroup is the answer, I wrapped my PortalDialog component with CSSTransition component as shown in their codesandbox sample.
<CSSTransition
in={isOpen}
timeout={300}
classNames="dialog"
unmountOnExit
>
<Portal>
<DialogWrapper>
{children}
<button onClick={() => setIsOpen(false)}>Close</button>
</DialogWrapper>
</Portal>
</CSSTransition>
Transition is now working without being created on every render.

Image not showing up in react js using require.context

I tried to search for the answer but I am not getting anything to solve it.
I am loading my image using require.context as you can see in the code but it's not getting loaded. It used to work perfectly before in previous versions of react js. Now I am using react version 17.0.1. There are no errors in the console. If I import the image and use it in the src it works fine. I have also tried to change the images with some previous images used in previous projects (using react version 16.x.x) which are working fine there. I am creating react app using npx-create-react-app. Path to image is correct as in case of incorrect path "module named xxx not found error occurs".
Current behavior:
Image not showing up instead alt value is showing up.
Desired behavior:
Image should show up instead of alt value.
import React, { Component } from "react";
import commonStyles from "../css/common.module.css";
import loginStyles from "../css/login.module.css";
import { TextField, Button, Paper, Typography } from "#material-ui/core";
class Login extends Component {
state = {
userName: "",
password: "",
error: "",
};
render() {
const images = require.context("../images", true);
return (
<div
className={`${loginStyles.root} d-flex justify-content-center align-items-center ${commonStyles.bg}`}
>
<Paper
classes={{
root: `${commonStyles.paper} mt-2`,
}}
elevation={3}
>
<div className={`${loginStyles.child}`}>
<div className={`d-flex justify-content-center align-items-center`}>
<img
src={images(`./Shahmeer.png`)}
alt={`Shahmeer Avenue Logo`}
width="100"
height="100"
/>
</div>
<Typography
classes={{
root: `font-weight-bold`,
}}
variant="h5"
gutterBottom
>
Login
</Typography>
<form noValidate autoComplete="off">
<TextField
classes={{
root: `${commonStyles.textField}`,
}}
onChange={(e) => this.handleChange(e)}
id={"userName"}
label={"User Name"}
variant="outlined"
error={this.state.error ? true : false}
helperText={this.state.error}
value={this.state["userName"]}
/>
<TextField
classes={{
root: `${commonStyles.textField}`,
}}
onChange={(e) => this.handleChange(e)}
id={"password"}
label={"Password"}
variant="outlined"
error={this.state.error ? true : false}
helperText={this.state.error}
value={this.state["password"]}
/>
<div className={`w-100 d-flex justify-content-end mt-2`}>
<Button variant="contained" color="primary">
Login
</Button>
</div>
</form>
</div>
</Paper>
</div>
);
}
}
export default Login;
snapshot of browser:
You should use the default property for the images:
<img
src={images(`./Shahmeer.png`).default}
alt={`Shahmeer Avenue Logo`}
width="100"
height="100"
/>

Resources