react-transition-group transition initial render without props/state - css

I'm currently implementing a Modal component within my app, using Portals. What I'm trying to achieve is that when the Modal component is rendered, it should fade in and when it's no longer rendered it should fade out.
Looking at the react-transition-group docs it appears that props have to be used in order to achieve this, however I'd like a solution without any form of state or props. Consider:
<App>
<Modal />
</App>
App should load as normal, however Modal should fade in. When Modal is no longer rendered based on some condition way above these components, it should fade out.
Here's the actual code for
import Fade from "../Fade";
import { TransitionGroup } from "react-transition-group";
const Modal = ({ children, ...other }) => {
return (
<Portal>
<TransitionGroup>
<Fade>
{children}
</Fade>
</TransitionGroup>
</Portal>
);
};
And for the Fade component:
import { CSSTransition } from 'react-transition-group';
const Fade = ({ children, ...other }) => (
<CSSTransition
{...other}
timeout={500}
classNames={{
//my classes here
}}
>
{children}
</CSSTransition>
);
Any suggestion? I'm sure this used to work, before React 16 and react-css-transitions changed to react-transition-group but I'm having a very hard time figuring this out.
To be clear, I can transition using state/props and have a working example of this but that is not what I'm trying to achieve. I'd like to fade in when it is rendered, and fade out when it is un-rendered...
Thanks!

appear={true}
Seems to fix this for now, although there's no way to animate the reverse (component being un-mounted).

Related

changing css code and setting focus at the same time (why doesn't it work?)

I tried to make...
There is no input on the screen at the first rendering. When I click the button, input appears. And I want to set focuse on the input at the same time.
Let me explain what i made.
At first, the input is not visible on the screen.
Because the display property of the Box(the div tag), which is the parent component of the input, is none.
But when i click the button, the display property of the Box changes to block.
And here is what i want to do.
i'm going to set focus on the input on the screen.
In the function called when the button is clicked, I wrote a code that changes the css code and sets the focus on the input.
But it didn't work.
Please take a look at the following code.
const [inputDisplay, setInputDisplay] = useState("none");
const refInput = useRef(null);
const HandleShowInput = () => {
setInputDisplay("block");
refInput.current.focus();
};
return (
<>
<Box theme={inputDisplay}>
<Input ref={refInput}/>
<Box/>
<Button onClick={HandleShowInput}/>
</>
)
Below is the code that is dynamically changing the css of the Box component.
import styled, { css } from "styled-components";
const Box = ({ children, ...props }) => {
return <StBox {...props}>{children}</StBox>;
};
const StBox = styled.div`
${({ theme }) => {
switch (theme) {
case "block":
return css`
display: ${theme} !important;
`;
default:
break;
}
}}
`;
export default Box;
But this below code is worked. I separated the code by putting it in useEffect.
const [inputDisplay, setInputDisplay] = useState("none");
const refInput = useRef(null);
const HandleShowInput = () => {
setInputDisplay("block");
};
useEffect(() => {
refInput.current.focus();
}, [inputDisplay]);
return (
<>
<Box theme={inputDisplay}>
<Input ref={refInput}/>
<Box/>
<Button onClick={HandleShowInput}/>
</>
)
I want to know why the upper case not works and the lower case works. I don't know if I have lack react knowledge or css knowledge. I would be very grateful if you could help a beginner in react. Also, please understand if there are any unnatural sentences because i'm not good at English. thank you.
When you are trying to focus on the input element by HandleShowInput this function.Here two things are happening your changing the state and focus of input.It will focus the input but time will be so less that we can't see on the ui.And also due to the state change render will happen and again ref will get the input element. Thus you are not able to see this focussed input
But in case of useEffect this will happen after the render. After this no rendering. So we can see the focussed input
The way of thinking about React is a little different from Javascript.
You may expect the below two run in the same way.
setInputDisplay("block");
refInput.current.focus();
and
document.querySelector('#canFocus').style.display='block'
document.querySelector('#canFocus').focus();
NO~ It's not.
JS block the Dom and then focus it, it works well.
But React works like the code below.
setTimeout(()=>{
// next react render cycle callback
document.querySelector('#canNotFocus').style.display='block'
}, 1000)
document.querySelector('#canNotFocus').focus();
While focus method is called, the dom is display as none;
You set state in react, ReactDom will make it as a display block in the next life cycle of function component.
demo here : https://codesandbox.io/s/confident-wilson-q01ktj?file=/index.html
useEffect(() => {
refInput.current.focus();
}, [inputDisplay]);
is a watching function. While inputDisplay changed, the function inside will be called.
you set state to block
react re-render the component as a newer state
render function called, and dom is block
Effect watching function is called and the focus() called.

Toggle Function Using 2 Separate Buttons in 2 Different Components for Mobile Navigation

I have a button in the Header component to toggle the mobile nav open and that is working fine. I have a instance of this button within the mobile menu that I want to close the mobile nav. Clicking the 2nd instance of the mobile nav toggle button in the mobile nav gives an error: Typeerror toggleMobile is not a function
I know I can make this work with a single instance of the button, but would really like to figure out how to get this working with two instances of the button.
Here is simplified code from the 3 components involved.
Simplified Header.js - this part is working as expected
export default function Header() {
const [mobileOpen, setMobileOpen] = useState(false)
const toggleMobile = (toggleValue) => {
setMobileOpen(toggleValue)
}
return (
<Box>
{mobileOpen && <MobileNav toggleMobile={toggleMobile} />}
<MobileIcon toggleMobile={toggleMobile} toggleValue={true} />
</Box>
)
}
MobileIcon component - this works when triggered from the Header component
function MobileIcon({ toggleMobile, toggleValue }) {
return (
<Link href="#">
<Icon
onClick={() => toggleMobile(toggleValue)}
display={{base: 'flex', md: 'none'}}
as={RiMenu2Line} w={8} h={8} mr={8}>
</Icon>
</Link>
)
}
export default MobileIcon;
Simplified MobileNav component - this is the problem. Both Header and MobileNav components are rendering the same MobileIcon component
function MobileNav( toggleMobile ) {
return (
<MobileIcon toggleMobile={toggleMobile} toggleValue={false}/>
)
}
export default MobileNav;
Clicking this instance of the MobileIcon gives the error: 'TypeError: toggleMobile is not a function' and references the toggleMobile function in the MobileIcon component.
I'm guessing this has something to do with how I'm passing the function reference twice in Header. I've tried a lot of different solutions, and can't figure it out.

Material-UI Select (or Select styling) with a custom Popper?

Problem:
I've been trying to figure out the right/best way to use a Material-UI Select component with a customized Popper for its dropdown menu. What I'm hoping to do is make something that looks like the following (but with Material-UI components or keeping the styling of a Material-UI Select component with the 'outlined' variant):
Options I've considered:
1. Simply customize the children of the Select component.
I've tried the following, however, it seems like the Popper component still closes if it is clicked on anywhere and I can't seem to figure out how to stop that.
import * as React from "react";
import { FormControl, InputLabel, Select } from "#mui/material";
export default function BasicSelect() {
return (
<FormControl sx={{ minWidth: 120 }}>
<InputLabel id="demo-simple-select-label">Dropdown</InputLabel>
<Select
label="Dropdown"
MenuProps={{
sx: { marginLeft: "-8px" },
onClick: (e) => { e.preventDefault(); e.stopPropagation(); }
}}
>
<div onClick={(e) => { e.preventDefault(); e.stopPropagation(); }}>
Test
</div>
</Select>
</FormControl>
);
}
2. Apply Select component css components/styles to a button or div that opens a Popper.
To start with, I tried the following but it didn't seem to apply the MUI select styling to the div:
import * as React from "react";
export default function Test() {
return (
<div className={`MuiSelect-select`}>
Test
</div>
);
}
Probably for obvious reasons that the element the class is being applied to is not a select (but I don't want it to be).
3. Write out css from scratch that tries to mimic the Select styling as close as possible.
Would be cumbersome, but potentially doable, I suppose.
What would you suggest or recommend? Thanks!

React onMount animations

I'm trying to create an onMount animation using React (and Tailwind but it doesn't matter). My current implementation is this:
const Component = () => {
const [mounted, setMounted] = useState(false)
useEffect(() => {
setTimeout(() => {
setMounted(true)
}, 250)
}, [])
return(
<nav
className={classNames(
'flex justify-between items-center transform-gpu transition-transform duration-500',
mounted ? 'translate-x-0' : '-translate-x-full')}> Some nav components </nav>
)
}
Basically, this code timeouts the state's change, which indicates when the component is mounted, and then applies CSS translate to the element.
I'm thinking about optimizing the current solution but was wondering if there are any other ways to do an onMount animations. I appreciate any advice.
I can create a SandBox example if that's necessary.
Not sure if this precisely answers your question, but I personally like to use framer-motion for stuff like this. For example, if you want to apply an animated translateX once the component is mounted you could do something like this:
import { motion } from "framer-motion";
function Component() {
return (
<motion.nav
initial={{ translateX: 0 }}
animate={{ translateX: 100 }}
transition={{ ease: "easeOut", duration: 2 }}
>
Some components
</motion.nav>
);
}
See this codesandbox. (Click the refresh button within the codesandbox-browser to retrigger the animation or use the mount/unmount button)
So instead of <nav> just use <motion.nav> and specify the animation via the framer-motion props. You can still style that element with other classes as before if you need them in addition.
Note that an even shorter way of expressing that is using the x property, as shown here:
<motion.nav
animate={{ x: 100 }}
transition={{ ease: "easeOut", duration: 2 }}
>
Some components
</motion.nav>
You can control the animations directly as attributes or use Animate Presence to control an animation for the component when it is unmounted.

Button with image using React Typescript

I am using React Typescript.
How do I add image to my button?
I tried doing it and there are no errors, but I am getting a blank button.
How do I style my button element using Emotion CSS here?
import React from "react";
function PostButton(props){
let style = {
width:24,
height:24,
src: "../images/abc.png"
};
return(
<button style={style} onClick={() => props.handleClick()}>{props.label}</button>
);
}
return (
<div style={style}>
<PostButton src={"../images/abc.png"} handleClick = {props.incrementScore}/>
</div>
);
}
src would be background:url(../images/abc.png)
Perhaps you're confusing the src prop that an img HTML element would have?
Perhaps you should include a nested img component inside your button.
Working example: https://stackblitz.com/edit/react-emotion-hello-u9qyaa
Damian's answer is technically correct however if you're using webpack you'll need to import the image like this:
import abc from "../images/abc.png";
And use it like this:
function PostButton(props){
let style = {
width:24,
height:24,
background: `url(${abc})`,
};
return(
<button style={style} onClick={() => props.handleClick()}>{props.label}</button>
);
}

Resources