In my project appear works well. However, I can not understand why enter and leave do not work.
The same happens with routers. When the page loads, the animation works. But when you switch to another page, everything happens abruptly.
What could be the problem?
import React, { Component } from "react";
import ReactCSSTransitionGroup from 'react-addons-css-transition-group';
import InputMask from 'react-input-mask';
import Close from '../images/cancel.svg';
import './fonts.css';
import './Modal.css';
class Modal extends Component {
state = {
email: '',
name: '',
phone: '',
message: ''
};
handleSubmit = (e) => {
e.preventDefault();
console.log('form is submitted. Email value is ', this.state);
e.target.reset();
};
handleInputChange = (e) => {
this.setState({[e.target.name]: e.target.value})
};
onClose = (e) => {
this.props.onClose && this.props.onClose(e);
};
render() {
if( !this.props.show ) {
return null;
}
return (
<div>
{this.props.children}
<ReactCSSTransitionGroup
transitionName="OverlayTransition"
//transitionAppear={true}
//transitionAppearTimeout={1300}
transitionEnterTimeout={1300}
transitionLeaveTimeout={1300}
>
<div className="Modal-block-wrap" onClick={(e) => { this.onClose(e) }}> </div>
</ReactCSSTransitionGroup>
<ReactCSSTransitionGroup
transitionName="WrapTransition"
transitionAppear={true}
transitionAppearTimeout={700}
>
<div className="Modal-block-wrapper"> </div>
</ReactCSSTransitionGroup>
<ReactCSSTransitionGroup
transitionName="ModalTransition"
transitionAppear={true}
transitionAppearTimeout={1300}
>
<div className="Modal-block">
<button className="Modal-block-close" onClick={(e) => { this.onClose(e) }} type="button">
<img src={Close} alt="close"/>
</button>
<div className="Modal-form">
<form onSubmit={this.handleSubmit} >
<h2>Some Text</h2>
<input
type="text"
name="name"
placeholder="Имя"
value={this.state.name}
onChange={this.handleInputChange}
required
/>
<input
name="email"
type="email"
placeholder="E-mail"
value={this.state.email}
onChange={this.handleInputChange}
required
/>
<InputMask
name="phone"
type="tel"
mask="+38(999)999-99-99"
placeholder="Телефон"
maskChar=" "
value={this.state.phone}
onChange={this.handleInputChange}
required
/>
<input
name="message"
type="text"
placeholder="Сообщение"
value={this.state.message}
onChange={this.handleInputChange}
/>
<button>Отправить</button>
</form>
</div>
</div>
</ReactCSSTransitionGroup>
</div>
);
}
}
export default Modal;
My css style
.OverlayTransition-appear {
opacity: 0.01;
}
.OverlayTransition-appear.OverlayTransition-appear-active {
opacity: 1;
transition: all 0.4s ease;
transition-delay: 0.9s;
}
.WrapTransition-appear {
opacity: 0.7;
transform: translateY(100%);
}
.WrapTransition-appear.WrapTransition-appear-active {
opacity: 1;
transform: translateY(0);
transition: all 0.7s ease;
}
.ModalTransition-appear {
opacity: 0.8;
transform: translateX(100%);
}
.ModalTransition-appear.ModalTransition-appear-active {
opacity: 1;
transform: translateX(0);
transition: all 0.7s ease;
transition-delay: 0.6s;
}
Related
I want to make a reusable modal component with transitions, but CSSTransition doesn't work, i have been trying for many ways but nothing works. Maybe is createPortal, or useContext.
I am interested in creating a single modal component for several pages, and only place the transition once so that it is reusable
Route
const App = () => {
const initialState = useInicialState();
return (
<AppContext.Provider value={initialState}>
<AdminProvider>
<Routes>
<Route exact path="/" element={<Login />} />
<Route element={<Layout />}>
<Route exact path="/dashboard" element={<Dashboard />} />
<Route exact path="/areas" element={<Areas />} />
<Route path="*" element={<NotFound/>} />
</Route>
</Routes>
</AdminProvider>
</AppContext.Provider>
);
};
export default App;
Hook:
const useInicialState= ()=>{
const [openModal, setOpenModal] = useState(false);
return{
openModal,
setOpenModal
}
}
export default useInicialState;
Context:
import React from "react";
const AppContext = React.createContext({});
export default AppContext;
Modal Component:
const Modal = ({children}) => {
const {openModal, setOpenModal}= useContext(AppContext)
const nodeRef = useRef(null);
console.log(openModal)
const handleClose = ()=>{
setOpenModal(false)
}
return (
ReactDOM.createPortal(
<div className="modal-background">
<CSSTransition
in={openModal}
timeout={500}
classNames="modal"
unmountOnExit
nodeRef={nodeRef}
>
<div className="modal" ref={nodeRef}>
<button onClick={handleClose} className="button-close">
x
</button>
{children}
</div>
</CSSTransition>
</div>,
document.getElementById('modal')
)
);
}
export default Modal
Page:
const Areas = () => {
const [token] = useContext(AdminContext);
const {openModal, setOpenModal} = useContext(AppContext);
const [sectors, setSectors] = useState([]);
const getSectors = async ()=>{
const requestOptions = {
method: "GET",
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + token,
},
};
const response = await fetch("/api/sector/list", requestOptions);
if(!response.ok){
}else{
const data = await response.json();
setSectors(data);
}
};
useEffect(() => {
getSectors();
}, [])
const handleModal= ()=>{
setOpenModal(!openModal)
}
return (
<>
{openModal && (
<Modal>
<p>esto es una prueba</p>
</Modal>
)}
<button className="button-create" onClick={handleModal}>Crear área</button>
<table className="styled-table">
<thead>
<tr>
<th>Nombre</th>
<th>Descripción</th>
<th>Modificar/borrar</th>
<th>Administrar personal</th>
<th>Administrar aspectos</th>
</tr>
</thead>
<tbody>
{sectors.map((sector)=>(
<tr key={sector.sector_name}>
<td>{sector.sector_name}</td>
<td>{sector.sector_description}</td>
<td>
<img src={edit} alt="" />
<img src={garbage} alt="" />
</td>
<td>
<img src={personal} alt="" />
</td>
<td>
<img src={drop} alt="" />
</td>
</tr>
))}
</tbody>
</table>
</>
)
}
export default Areas;
and last Css:
.modal-background{
display:block;
position: fixed;
z-index: 1;
padding-top:100px;
left: 0;
top: 0;
width:100%;
height: 100%;
overflow: auto;
background-color: rgb(0, 0, 0);
background-color: rgba($color: #000000, $alpha: 0.5);
}
.button-close{
position: absolute;
left:200px;
}
.modal{
position:relative;
background-color:white;
margin:auto;
margin-left:267px;
padding:0;
border:none;
border-radius:10px;
}
.modal-enter{
opacity:0;
transform:scale(0);
}
.modal-enter-active{
opacity:1;
transform: scale(1);
transition: opacity 500ms, transform 500ms;
}
.modal-exit{
opacity: 1;
}
.modal-exit-active{
opacity:0;
transform: scale(0);
transition: opacity 500ms, transform 500ms;
}
Find myself the solution, thge problem was by the conditional for CSStransition.
{openModal && (
<Modal>
<p>esto es una prueba</p>
</Modal>
)}
with out this condition work fine:
<Modal>
<p>esto es una prueba</p>
</Modal>
I'm attempting to use useState to alter the display type in my styled components. When attempting to use my code the display type is not altered and my variable "displayType" is undefined.
I've attempted altering what my setStyle() function returns, but I am starting to see this is a larger problem that I'd like to understand better.
When I print the value to the console in index.js everything works fine. However I just get undefined when I try to use displayType in StoreElements.js
src/pages/store.js
const [displayType, setDisplayType] = useState("none");
const setStyle = (displayType) => {
setDisplayType(displayType);
console.log(displayType)
};
const [isOpen, setIsOpen] = useState(false)
const toggle = () => {
setIsOpen(!isOpen)
}
return (
<div>
<Sidebar isOpen={isOpen} toggle={toggle} />
<Navbar toggle={toggle} />
<Store setStyle={setStyle} displayType={displayType}></Store>
<Footer />
</div>
)
}
export default StorePage
src/store/index.js
const Store = ({displayType, setStyle}) => {
return (
<>
<AboutBg style={{ backgroundImage: `url(${BgPic})` }}></AboutBg>
<StoreContainer>
<StoreWrapper>
<Title>Store</Title>
<ItemsContainer>
<ItemWrapper
onMouseEnter={() => setStyle("hoodie")}
onMouseLeave={() => setStyle("none")}
>
<ImgWrapper>
<ImgLink to="/about">
<MerchImg src={frontHoodie}></MerchImg>
</ImgLink>
</ImgWrapper>
<TextWrapper>
<MerchText>Hoodie text</MerchText>
<HoodiePriceText>price</HoodiePriceText>
</TextWrapper>
</ItemWrapper>
<ItemWrapper
onMouseEnter={() => setStyle("sweats")}
onMouseLeave={() => setStyle("none")}
>
<ImgWrapper>
<ImgLink to="/tournaments">
<MerchImg src={frontSweats}></MerchImg>
</ImgLink>
</ImgWrapper>
<TextWrapper>
<MerchText>Sweats text</MerchText>
<SweatsPriceText displayType={displayType}>
price
</SweatsPriceText>
</TextWrapper>
</ItemWrapper>
<ItemWrapper
onMouseEnter={() => setStyle("shirt")}
onMouseLeave={() => setStyle("none")}
>
<ImgWrapper>
<ImgLink to="/">
<MerchImg src={frontShirt}></MerchImg>
</ImgLink>
</ImgWrapper>
<TextWrapper>
<MerchText>Shirt text</MerchText>
<ShirtPriceText>price</ShirtPriceText>
</TextWrapper>
</ItemWrapper>
<ItemWrapper
onMouseEnter={() => setStyle("mousepad")}
onMouseLeave={() => setStyle("none")}
>
<ImgWrapper>
<ImgLink to="/">
<MerchImg src={mousepadFront}></MerchImg>
</ImgLink>
</ImgWrapper>
<TextWrapper>
<MerchText>mouspad text</MerchText>
<MousepadPriceText>price</MousepadPriceText>
</TextWrapper>
</ItemWrapper>
</ItemsContainer>
</StoreWrapper>
</StoreContainer>
<div>
{listItems}
{cartItems}
Total: ${cartTotal}
{cartItems.length}
</div>
</>
);
};
export default Store;
src/store/StoreElements.js
export const HoodiePriceText = styled.h4`
color: red;
position: absolute;
top: 365px;
transition: 0.8s all ease;
display: ${({ displayType }) => {
if (displayType === "hoodie") {
console.log("working");
return "block";
} else {
console.log({displayType})
return "none";
}
}};
`;
export const ShirtPriceText = styled.h4`
color: red;
position: absolute;
top: 365px;
transition: 0.8s all ease;
`;
export const MousepadPriceText = styled.h4`
color: red;
position: absolute;
top: 365px;
transition: 0.8s all ease;
`;
export const SweatsPriceText = styled.h4`
color: red;
position: absolute;
top: 365px;
transition: 0.8s all ease;
`;
In your styled component usage, you should bind the property displayType:
<HoodiePriceText displayType={displayType}>price</HoodiePriceText>
Thus, you should able get displayType in styled component!
setDisplayType is triggering the state change and causes a re-render of the function. It does not modify the value of the variable displayType. The value displayType is still undefined directly after calling setDisplayType, because it only gets its value after the function re-runs the useState-line.
const [displayType, setDisplayType] = useState("none");
// displayType can only get a new value here
Can't seem to get the alert to disappear in 2 seconds. The error message prop pulls from the state and it's generating correctly. Also other implementations are welcome, doesn't have to be using this method. Let me know if more information is needed. Thanks!
In mystyle.module.css file:
#hideMe {
-moz-animation: cssAnimation 0s ease-in 2s forwards;
/* Firefox */
-webkit-animation: cssAnimation 0s ease-in 2s forwards;
/* Safari and Chrome */
-o-animation: cssAnimation 0s ease-in 2s forwards;
/* Opera */
animation: cssAnimation 0s ease-in 2s forwards;
-webkit-animation-fill-mode: forwards;
animation-fill-mode: forwards;
}
In React file:
import hideMe from './mystyle.module.css';
.
.
.
render() {
return (
<div className={s.root}>
<Row>
<Col md={12} sm={12} xs={12}>
{this.props.errorMessage && (
<Alert type={hideMe.hideMe} size="sm" color="danger">
{this.props.errorMessage}
</Alert>
)}
{this.props.successMessage && (
<Alert id="hideMe" size="sm" color="success">
{this.props.successMessage}
</Alert>
)}
<Widget>
<FormGroup>
<Label for="input-userlogin">User Login</Label>
{!this.state.changingLogin ? <Input
id="input-userlogin"
type="text"
placeholder="User Login"
value={this.state.userLogin}
onChange={this.changeUserLogin}
/> : <div><h6>{this.state.userLogin}</h6></div> }
</FormGroup>
<div className="d-flex justify-content-end">
<ButtonGroup>
<Link to="/app/users"><Button color="default">Cancel</Button></Link>
{this.props.selectedUser ?
<Button color="danger" onClick={this.doUpdateCreateLogin}>
{this.props.isCreated ? 'Updating...' : 'Update'}
</Button> :
<Button color="danger" type="submit">
{this.props.isCreating ? 'Creating...' : 'Create'}
</Button>}
</ButtonGroup>
</div>
</Widget>
</Col>
</Row>
</div>
);
}
}
function mapStateToProps(state) {
return {
selectedUser: state.users.selectedUser,
errorMessage: state.users.message,
isFetching: state.users.isFetching,
};
}
An alternative, very simple implementation is to just work with a state that determines the visibility of the component and changing its value with a timeout in a useEffect hook (or the componentDidMount method for class components).
const { useState, useEffect } = React;
const Alert = () => {
const [show, setShow] = useState(true);
useEffect(() => {
setTimeout(() => {
setShow(false);
}, 3000);
}, []);
return show && <div>I will be hidden after 3s</div>;
};
const App = () => {
return <Alert />;
};
// Render it
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I have almost got this workign but not quite sure what I am doing wrong. It will slide in when I click the toggle button, but it wont slide out when I click it again, it will just rerun the slide in animation.
Any help would be great
I have the following state and toggle function
const [close, setClose] = useState(false)
const toggleCart = () => {
setClose(!close)
}
following component
<CartItems close={close} location={location} />
import React, { useState } from "react"
import tw, { styled } from "twin.macro"
import { useTransition, animated } from "react-spring"
const CartWrapper = styled.div`
.test {
position: fixed;
top: 0px;
z-index: 5000;
right: 0;
height: 100vh;
background: lightgrey;
padding: 25px;
}
`
export function CartItems({ location, close }) {
const transitions = useTransition(close, null, {
enter: { transform: "translate3d(100%,0,0)" },
leave: { transform: "translate3d(0%,0,0)" },
})
return (
<>
<CartWrapper>
{transitions.map(({ props }) => {
return (
<animated.div className="test" style={props}>
<h2>Shopping Cart</h2>
{cart}
<p>Total: {formattedTotalPrice}</p>
<form onSubmit={handleSubmitCheckout}>
{/* include validation with required or other standard HTML validation rules */}
<input
name="name"
placeholder="Name:"
type="text"
onChange={e => setName(e.target.value)}
/>
<input
name="giftMessage"
placeholder="Gift Message:"
type="text"
onChange={e => setGiftMessage(e.target.value)}
/>
<input type="submit" />
</form>
<button onClick={clearCart}>Remove all items</button>
</animated.div>
)
})}
{/* <button onClick={handleSubmit}>Checkout</button> */}
</CartWrapper>
</>
)
}
In your example there is a second item during the transition, one entering, and one leaving. That's why you see always the entering animation.
If you use a boolean instead of array in the useTransition you have to insert a condition in the render method to prevent the second item. Just like the third example in the useTransition doc. https://www.react-spring.io/docs/hooks/use-transition
transitions.map(({ item, props, key }) => {
return (
item && <animated.div className="test" style={props} key={key}>
Now it basically works, but a slight modification in the useTransition is necessary.
const transitions = useTransition(close, null, {
from: { transform: "translate3d(100%,0,0)" },
enter: { transform: "translate3d(0%,0,0)" },
leave: { transform: "translate3d(100%,0,0)" }
});
I have a working example here: https://codesandbox.io/s/toggle-react-spring-transition-ju2jd
I may be thinking about this the wrong way. So any guidance would be very useful.
I have an element, then when you click opens up a number of radio buttons. Clicking one of the radio buttons triggers a setTimeout of 2300ms this will add a class Frequency--closing to an element Frequency which triggers an animation in the css, when the timeout finishes, that component gets removed from the DOM.
I am using the timeout as means to allow an animation before it gets removed from the DOM.
What I am aiming for is when I click a radio button and then click another radio button before the component gets removed from the DOM that the timer resets, so on the 2nd click on it waits another 2300ms before it gets removed from the DOM.
Below is the component that uses state to dictate whether the element is opened or closed with the onToggle() function
const SavedSearchBox = ({ modifier, search, loading }) => {
const [frequencyToggled, setFrequencyToggled] = useState(false);
const [closeFrequency, setCloseFrequency] = useState(false);
const timeoutRef = useRef(null);
const onToggle = () => {
if (frequencyToggled) {
setCloseFrequency(true);
timeoutRef.current = setTimeout(
() => setFrequencyToggled(!frequencyToggled),
2300
);
} else {
setCloseFrequency(false);
setFrequencyToggled(!frequencyToggled);
clearTimeout(timeoutRef.current);
}
};
useEffect(() => () => clearTimeout(timeoutRef.current), []);
return (
<div
className={`SavedSearchBox ${modifier ? modifier : ""} ${
loading && loading.status ? "SavedSearchBox--loading" : ""
}`}
>
<div className="SavedSearchBox-footer">
{frequencyToggled ? (
<Frequency
closeFrequency={closeFrequency}
onChange={() => onToggle()}
/>
) : (
<div onClick={() => onToggle()}>Click Here</div>
)}
</div>
</div>
);
};
The onToggle() function also gets called from the child component Frequency. This component also includes the class Frequency--closing for the closing animation.
const Frequency = props => {
const [active, setActive] = useState(0);
const inputs = [
["Instant Alerts", "Instant"],
["Twice Daily Alerts", "Twice"],
["Daily Alerts", "Daily"],
["Weekly Alerts", "Weekly"],
["No Email Alerts", "None"]
];
return (
<div
className={`Frequency ${
props.closeFrequency ? "Frequency--closing" : ""
}`}
>
<div className="Frequency-list">
{inputs.map(([text, value], i) => (
<div key={i} className="Frequency-listItem">
<label className="Frequency-listLabel">
{text}
<input
type="radio"
checked={value === inputs[active][1]}
onChange={() => {
props.onChange();
setActive(i);
}}
value={value}
/>
<span className="Frequency-listRadio" />
</label>
</div>
))}
</div>
</div>
);
};
Here are the animations in the case that it is that is perhaps causing this issue
.Frequency-listItem {
animation: enter $Frequency-duration $Frequency-easing backwards;
transform: translateY(0%);
transition: transform $Frequency-duration ease;
display: flex;
justify-content: left;
align-items: center;
#for $i from 1 through 6 {
&:nth-of-type(#{$i}) {
animation-delay: calc(#{$i} * #{$Frequency-delay});
}
}
}
.Frequency--closing .Frequency-listItem {
animation: exit $Frequency-exitDuration $Frequency-easing both 1;
transform: translateY(100%);
transition: transform $Frequency-exitDuration ease;
#for $i from 6*-1 through -1 {
&:nth-of-type(#{abs($i)}) {
animation-delay: calc(#{abs($i)} * #{$Frequency-exitDelay} + 2s);
}
}
}
#keyframes enter {
from {
opacity: 0;
padding: 0;
margin: 0;
max-height: 0;
transform: scale(.7);
}
}
#keyframes exit {
from {
opacity: 1;
max-height: 100%;
transform: scale(1);
}
to {
opacity: 0;
padding: 0;
margin: 0;
max-height: 0;
transform: scale(.7);
}
}
Here is a CODESANBOX if you wish to test it out
Any help would be greatly appreciated!