I have a problem with React transitions group. For some reason, the fade in and out is not working on Router Switch. I have read the documentation and implemented it inside my App.
Don't know what is wrong with my code.
My React code:
const App = () => (
<Router>
<Menu />
<TransitionGroup>
<CSSTransition
timeout={{ enter: 300, exit: 300 }}
classNames={'fade'}>
<Switch>
{routes.map(route => (
<Route
exact
key={route.path}
path={route.path}
component={route.component}
/>
))}
</Switch>
</CSSTransition>
</TransitionGroup>
</Router>
);
export default App;
Css code:
.fade-enter {
opacity: 0.01;
&.fade-enter-active {
opacity: 1;
transition: opacity 300ms ease-in;
}
}
.fade-exit {
opacity: 1;
&-active {
opacity: 0.01;
transition: opacity 300ms ease-in;
}
}
Nevermind, forgot to wrap it:
<Route render={({ location }) => (
Thanks.
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>
When entering the website, you will be in Home component with path /
But if you switch page to About/Dashboard, below error occurs
Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
How to fix it?
example.js
import React, { useEffect } from "react";
import { BrowserRouter as Router, Switch, Route, Link } from "react-router-dom";
import ScrollMagic from "scrollmagic";
export default function BasicExample() {
return (
<Router>
<div>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About</Link>
</li>
<li>
<Link to="/dashboard">Dashboard</Link>
</li>
</ul>
<hr />
<Switch>
<Route exact path="/">
<Home />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
</Switch>
</div>
</Router>
);
}
function Home() {
useEffect(() => {
const scrollController = new ScrollMagic.Controller({
globalSceneOptions: { triggerHook: "onCenter" }
});
const scrollScene = new ScrollMagic.Scene({
triggerElement: "#pin",
duration: 2000,
offset: -50
})
.setPin("#pin")
.on("progress", function (e) {
console.log(e.progress);
});
scrollScene.addTo(scrollController);
}, []);
return (
<div id="pin">
<h2>Home</h2>
<div style={{ height: "1700px" }}>Another Stuff</div>
</div>
);
}
function About() {
return (
<div>
<h2>About</h2>
</div>
);
}
function Dashboard() {
return (
<div>
<h2>Dashboard</h2>
<div style={{ height: "1700px" }}>Another Stuff in Dashboard</div>
</div>
);
}
Codesandbox
https://codesandbox.io/s/react-router-basic-forked-q9iwm?file=/example.js
I dont know what you are trying to achieve with ScrollMagic, but the following block definitely works.
<React.Fragment id="pin">
<h2>Home</h2>
<div style={{ height: "1700px" }}>Another Stuff</div>
</React.Fragment>
Sandbox Link
Just to add, when I debugged, it looked like that when you are using setPin function, it is causing some issues.
The problem is in About/Dashboard function can you replace your div tag with <React.Fragment> and try. Fragments let you group a list of children without adding extra nodes to the DOM.
function About() {
return (
<React.Fragment>
<h2>About</h2>
</React.Fragment>
);
}
It seems the issue is with the useEffect code in your home component.
I commented that out and all seems well. See codesandbox https://codesandbox.io/s/react-router-basic-forked-7oysy?file=/example.js
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'm not gettin the blue color to show up on the toolbar when I run this code. The toolbar loads with the tabs properly, but there's no theming. It's just a grey and dark greyish color. I'm expecting the lightbase theme from Material-UI#next
My main app.js file looks like this:
const theme = createMuiTheme();
const render = (messages) => {
ReactDOM.render(
<Provider store={store}>
<LanguageProvider messages={messages}>
<ConnectedRouter history={history}>
<MuiThemeProvider theme={theme}>
<Switch>
<Route exact path="/" component={Splash} />
<Route exact path="/main" component={App} />
<Route path="/main/:navmain" component={App} />
</Switch>
</MuiThemeProvider>
</ConnectedRouter>
</LanguageProvider>
</Provider>,
MOUNT_NODE
);
};
Then, when i navigate to /main, this is what I have:
const AppWrapper = styled.div`
max-width: calc(768px + 16px * 2);
margin: 0 auto;
display: flex;
min-height: 100%;
padding: 0 16px;
flex-direction: column;
`;
class App extends React.Component {
render() {
return (
<div>
<Header />
<AppWrapper>
Below this are where the new components should render.
<Switch>
<Route path="/main/aboutme" component={AboutMe} />
<Route path="/main/models" component={Models} />
<Route path="/main/landscapes" component={Landscapes} />
</Switch>
<Footer />
</AppWrapper>
</div>
);
}
}
For some reason I'm having a tough time seeing what I'm doing wrong here. I am wrapping the routes in the main switch statement with the MuiThemeProvider and expecting anything inside of that component (like the routes) to inherit the theme from Material UI.
Can anyone help me out here?