Conditional Rendering the components, Next.js style broken on first load - css

I am using Ant Design and Custom Stylesheet. On first load style is broken, but when I visit another page and come back to broken page, now it looks fine. So the problem is only on the first load. It's on the development server, I have been clear all cache. But still the same issue.
Here is the screenshot how it's looking like after first load
Here is the correct style after I come back from another page
Here is the code how I am rendering the components:
<div>
{jwToken || role === "restaurant_owner" ? (
<Layout>
<Index />
</Layout>
) : (
<div>
<Login />
</div>
)}
</div>

I had a simliar issue, the way I fixed it was to add a mounted variable that depended on the condition. So it looks this.
// Not sure how you pass the condition, I'm assuming hooks
const { condition } = someHook()
const [mounted, setMounted] = useState()
useEffect(() => {
setMounted(true)
return () => setMounted(false)
}, [condition]);
return (
<div>
{mounted && condition && <Component/>
</div>
)
As to why this happens, I suspect it has to do with SSR (I found simliar issue on Github but for Material-UI) and my solution forces the condition to be available only during the browser.

Related

nextjs localStorage getItem

after searching half a day I still not able to getItem from local storage.
the idea is to save some data to local storage and based on that I want to route a user in the Layout component. I am able to save to local storage and delete but not able to get data from it. I get error 'local storage not defined' or 'destroy is not a function'
I have 3 components save, delete and get. save and delete I execute after a client side api call, the get function I need to be working in the Layout as it is the top level for all routes.
I Need a bit help to the right direction please.
---Upadte
I found something that works
export const IsAuth = ()=>{
const [auth, setAuth] = useState();
useEffect(()=>{
if(typeof windows === undefined) return;
const item = localStorage.getItem('ltu');
setAuth(!!item);
},[]);
return auth;
}
now my problem is I have not much understanding of nextjs. I used the Layout to create a theme template, I basically have only 3 pages that can be visited if not logged in and the rest one needs to be logged in. I get so many examples but it seems like I need to verify auth on every single page instead of being able to do this on root/layout level.
all examples I get are without the use of Layout and I am totally stuck.
I want a simple login system just with jwt and check if thats there to show pages.
I could not get the localStorage.getItem() to work in the layout template.
My solution while maybe not perfect is.
in the _app.js I create useState() and pass those along to the menu trough the Layout, in in the menu useEffect() with 'use client' in the useEffect I set the state I need global.
_app.js
export default function App({ Component, pageProps }){
const [isAuth, setAuth] = useState()
const [user, setUser] = useState()
return (
<Layout setAuth={setAuth} isAuth={isAuth} user={user} setUser={setUser}>
<Component user={user} setUser={setUser} isAuth={isAuth} {...pageProps} />
</Layout>
)
}
Layout.js
export default function Layout({ children, setAuth, isAuth, user, setUser }) {
return (
<>
<Headd />
<SideMenu setAuth={setAuth} isAuth={isAuth} user={user} setUser={setUser}/>
<main>
<div className="menu-spacer"></div>
<content>
{children}
</content>
</main>
</>
)
}
menu.js
'use client';
const SideMenu = ({setAuth, isAuth, user, setUser}) => {
useEffect(()=>{
if(typeof windows === undefined) return;
const item = localStorage.getItem('ltu');
setAuth(!!item);
if(item) setUser(JSON.parse(localStorage.getItem('Ud')))
}, [router, router.isReady])
}
Now I can use the {isAuth, user,} on any page and component.
I am pretty sure this is not the right solution, but I could not find any other working solution and no one here yet posted a answer.

Next.js: Passing data to nested routes

Issue:
Right now, I have a dynamic route that fetches data using getServerSideProps(). Within this page, there are multiple tabs that renders different data depending on state (which tab is selected).
I wish to transition from using multiple tabs on this page, to instead using nested routes. However, I am having difficulty obtaining the data originally fetched in these nested routes. Is there an efficient way of doing so, without having to call getServerSideProps() again?
My intended setup looks like this, where [page] calls getServerSideProps():
[page].jsx
|_tab1.jsx
|_tab2.jsx
|_tab3.jsx
My current [page].jsx, where I would like to use separate, nested pages that have access to these props (instead of rendering each tab based on state):
export default function Page(props) {
const [currentTab, setCurrentTab] = useState("home");
return (
<div>
<div id="tab1" onClick={() => setCurrentTab("home")}>
home
</div>
<div id="tab2" onClick={() => setCurrentTab("posts")}>
posts
</div>
<div id="tab3" onClick={() => setCurrentTab("info")}>
info
</div>
{currentTab === "home" ? (
<HomeTab props={props}/>
) : currentTab === "posts" ? (
<PostsTab props={props}/>
) : (
<InfoTab props={props}/>
)}
</div>
);
}
Attempts
I've attempted using the context API to utilize data globally, which my other pages can use. However, this requires the user to visit the original dynamic route first.
Call getServerSideProps() on each nested route. Although this works, I wish to find a better solution, since I'm fetching data on each nested route while the route they're nested under has all of this data available already.
You can use shallow routing in next/route or next/link
Note that, in the below example, I'm using next/link for the demonstration. Without your tab data, I'd assume you have an array of tabs in data
import { useEffect } from 'react'
import Link from 'next/link'
//the path is `/tab/:tabId`
function Tab({ data }) {
const [tabData, setTabData] = useState(data[0]) //first tab data as default for example
useEffect(() => {
setTabData(data[tabId])
}, [router.query.tabId])
return <>
<Link href="/tab/0" shallow />
<Link href="/tab/1" shallow />
<div>
{tabData}
</div>
</>
}
export async function getServerSideProps(context) {
return {
props: {
data: [], //tabs' data you fetched from the API
},
}
}
export default Tab

CSS crashes when I reload page

I am building a site with Gatsby.
I am using a component that imports a script and returns a form.
The problem is, that after you loaded the page that shows the form, and then you click to any other page and go back to that form page, the css fully crashes for the entire site and you have to refresh the whole page.
To check out what I mean click this link https://baerenherz.org/, go to the dark blue button on the very right of the menu, then click to any other navigation site and then click again on the blue button (jetzt-spenden).
Here is my component for the donation form :
import React, { useState, useEffect } from "react"
import {Helmet} from "react-helmet"
import Loading from "./Loading"
function Child() {
return(
<div style={{width: "75%", margin: "4em auto"}} >
<Helmet>
<script type='text/javascript' aysnc>
{` window.rnw.tamaro.runWidget('.dds-widget-container', {language: 'de'}) `}
</script>
</Helmet>
<div className="dds-widget-container"></div>
</div>
)
}
function RaiseNow() {
const [loaded, setLoaded] = useState(false)
useEffect(() => {
const scriptTag = document.createElement('script')
scriptTag.src='https://tamaro.raisenow.com/xxx/latest/widget.js'
scriptTag.addEventListener('load', ()=> setLoaded(true))
document.body.appendChild(scriptTag)
return ()=>{
scriptTag.removeEventListener(); // check if necessary
setLoaded(false) // check if necessary
}
}, []);
return (
<>
{loaded ? <Child /> : <Loading/>}
</>
)
}
export default RaiseNow
What I noticed is, that the second time you visit the page, the Loading.... component does not even show anymore.. the Layout is displayed but as soon as the form shows, it crashes...
Since I cannot solve this issue since literally last year I would really appreciate any help with this. Thank you in advance.
Apparently, your script is breaking React's hydration when the component should be mounted/unmounted. There's no "clean" solution if there's no React-based script available. The problem here is that your script is manipulating the DOM while React manages the virtual DOM (vDOM). Changes in the DOM outside React's scope are not listened to by React and vice versa.
That said, I'd try forcing the loading and rendering of your widget each time the page loads. Something like:
function RaiseNow() {
const [loaded, setLoaded] = useState(false)
useEffect(() => {
const scriptTag = document.createElement('script')
scriptTag.src='https://tamaro.raisenow.com/xxx/latest/widget.js'
scriptTag.addEventListener('load', ()=> setLoaded(true))
document.body.appendChild(scriptTag)
window.rnw.tamaro.runWidget('.dds-widget-container', {language: 'de'})
return ()=>{
scriptTag.removeEventListener('load', setLoaded(false)); // check if necessary
setLoaded(false) // check if necessary
}
}, []);
return (
<>
{loaded ? <Child /> : <Loading/>}
</>
)
}
export default RaiseNow
Without a CodeSandbox it's difficult to guess how the code will behave but what it's important is to detach and clean up the listeners when the component is removed from the UI to avoid breaking React's hydration process, in the return statement. From the useEffect docs:
The clean-up function runs before the component is removed from the UI
to prevent memory leaks. Additionally, if a component renders multiple
times (as they typically do), the previous effect is cleaned up before
executing the next effect. In our example, this means a new
subscription is created on every update. To avoid firing an effect on
every update, refer to the next section.
There, besides removing the listeners from the script, you can also set the loading state to false.
I've also removed the second useEffect because the idea to avoid the CSS breaking is to force the loading of the script in each page rendering. It's not an optimal solution but it may work for you. The ideal solution would be using React-based dependencies.
Another thing to take into account is to delay the trigger of your rnw.tamaro script until the DOM tree is loaded, by moving it from the Helmet to the useEffect. This should ensure that your div and the window are available.
Turns out it was a issue on their end. Since they did an update it works.

Dynamic routing results in 404

Following this guide, I created the following file in my project:
/pages/user/[id].js
class Post extends Component {
render() {
return (
<React.Fragment>
<Navbar />
<Content />
<Footer />
</React.Fragment>
);
}
}
export default Post;
But when I go to that URL, I get a 404.
What is the problem?
Assuming you're visiting (for example), http://localhost:3000/user/something (where something is your id), try also visiting http://localhost:3000/user/something/ (note the backslash). This is currently a known issue in Next with dynamic routing.
(This also assumes you don't have pages/user/something.js in your project as dynamic routes take a back seat to explicitly named routes.)

Rendering React Components to Specific Div Using Routing

I am using FlowRouter as a router for a Meteor/React application I am trying to create. I'm having a very hard time trying to get my react components to render in specific places. Does anyone know how to do this?
So on my landing page, when I click a button, I want to route to a secondary page. I have three different components that I want to render in certain parts of the page. I've been using ReactLayout.render(), but I can't seem to make sure components get rendered in certain areas. I thought document.getElementById would work
ReactLayout.render(LandingPage, document.getElementById("landing-page")
but it hasn't been.
The second parameter of ReactLayout.render expects an object. If you want to render several components into your LandingPage element, it might look something like this:
LandingPage = React.createClass({
render() {
return (
<div className="app-root">
<AppHeader />
<div className="container">
{this.props.testOne}
</div>
<div className="app-root">
{this.props.testTwo}
</div>
</div>
);
}
});
Then render using:
FlowRouter.route( '/testRedirect', {
name: 'test',
action() {
ReactLayout.render( Default, { testOne: <TestOneComponent />, testTwo: <TestTwoComponent /> } );
}
});

Resources