Render ReactJS app on demand by calling a JS function - javascript

I am wondering if it's possible to initialize react app in same way as a for example a jQuery component, since we need to create some widget with some options that would be so much easier in react than in pure JS/jQuery.
I have tried to wrap render(...) in a window.MyInitFunction = function ({....}) {render(<App />} then calling it in html, but it's undefined.
My goal is for example calling function
MyInitFunction({'selector': '.foo', 'someOtherProp': 'bar'});
and it will render the react app in the given selector which might be one or multiple elements.
I guess that there might be few problems if rendered to multiple elements since it will use the same js code for storing for example redux state,...
Goal is also to create a jQuery compatible extension from it in form of: $(".foo").MyInitFunction({.....});
Tried to google it, but nearly all results are irrelevant to what I want to achieve, mostly dynamically loading components, which is not my case.
$(".foo").MyInitFunction({.....});
Thank you in advance for any tips :)

You should be able to render a React app conditionally when you want. Check the snippet below. It renders the react app on click of the button.
// React Component
function App() {
return (
<div className="App">
<h1>Hello from React!</h1>
</div>
);
}
function renderReactApp() {
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById("root")
);
}
function init() {
document.getElementById("start").addEventListener("click", function() {
renderReactApp();
});
}
init();
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<body>
<button id="start">render react app from outside</button>
<div id="root"></div>
</body>

Related

I need to embed code in my Next.js site. (The next/script renders on all pages at the very bottom)

I feel like there should be a very simple solution to this and I can't seem to find one. I would like to embed code on my Next.js website, however, it will only work if I refresh the page. It does not load when I navigate to it via router/navbar link.
I tried to render it in useEffect() and useState(), however, the <script> doesn't seem to want to run in the useEffect() hook. I have not found a successful way to get dirty with it and use vanilla JavaScript in getServerSideProps() as suggested in another post, but I did find next/script in the documentation. This actually treats the script mostly how I need it to as far as when it renders. There is only one issue...
It does not render in the location I put it in the DOM. Instead it renders at the bottom of the webpage (even below the footer). I tried creating a component and then inserting it but that did not work either. This is because not only is it at the bottom of the current page, it's loading at the bottom of every page.
Any help is appreciated. Thank you!
Embedded component:
import Script from "next/script";
export default function paintCalculator() {
return (
<Script
async
type="text/javascript"
id="paint-calculator-tool"
data-type="dotdash-tool"
data-vertical="thespruce"
src="https://www.thespruce.com/static/5.174.0/static/components/widgets/iframe-embed/embed.min.js?id=paint-calculator-tool"
strategy="afterInteractive"
dangerouslySetInnerHTML={{
__html: `!function(){var e,i,n,a,t=document.getElementsByTagName("script");[].forEach.call(t,function(t){"dotdash-tool"===t.getAttribute("data-type")&&(e=t.parentElement,i=document.createElement("iframe"),n=t.getAttribute("id"),a=t.getAttribute("data-vertical"),i.src="https://www."+a+".com/tools/"+n,i.style.border="none",i.id=n,i.style.width="100%",t.nextSibling&&t.nextSibling.id===n||e.insertBefore(i,t.nextSibling))}),window.addEventListener("message",function(e){var t=document.getElementsByTagName("iframe");[].forEach.call(t,function(t){t.getAttribute("id")===e.data.embedId&&(t.style.height=e.data.height+10+"px")})},!1)}();`,
}}
/>
);
}
Page with imported component:
import styles from "../styles/Home.module.css";
import React from "react";
import PaintCalculator from "../components/PaintCalculator";
export default function paintCalculator() {
return (
<div className={styles.pageContainer}>
<div>
<h1>Paint Calculator</h1>
<PaintCalculator />
</div>
</div>
);
}

Menu items get from json (created server side, Nextjs) don't appear in HTML source code

sorry if the title is not very clear.
I try to explain myself: my site is built with Next.js and retrieves data from Sanity Studio (CMS).
In Sanity the user can create menu items.
I have a menu in the footer component, one in the sidebar component and another one in the header component. The thing is, I can fetch sanity only in pages. So I created a function that, with each build, creates a JSON with all the information entered by the user.
Next, to have the menu items throughout the site, this JSON is imported into the "Layout" component and stored in a specific react context.
And everything works fine. But I noticed one thing: if I look in the source code of the page, the menu looks like this:
<nav>
<ul></ul>
</nav>
even though it is rendered perfectly in HTML.
I guess it is because I generate the JSON server side and when the page is created that information is not available.
Any ideas?
Thank you
EDIT: this is the Layout component where I imported the JSON file
import styles from '#styles/components/Layout.module.css';
import React from 'react';
//other imports...
//imported JSON
import globalData from '#client/global-manifest.json';
//this is the normalize function
import { normalizeNavigationFromRaw } from '#utils/normalizeNavigation';
//this is the navigation normalized
const navigationData = normalizeNavigationFromRaw(globalData?.navigation ?? {});
export default function Layout({ children }: { children: React.ReactElement }) {
// some functions for open/close modal ...
//context method to store navigation
const { setNavigation } = React.useContext<any>(GlobalSettingsContext);
//store navigation in context
React.useEffect(() => {
setNavigation(navigationData);
}, []);
return (
<>
<Head>
// some head code
</Head>
<main className={styles.container}>
<HeaderWrapper
toggleMenu={toggleMenu}
toggleContactPanel={toggleContactPanel}
/>
<SidebarMenu
isOpenMenu={isOpenMenu}
toggleMenu={toggleMenu}
ref={sideMenuRef}
/>
<ContactPanel
isOpenContact={isOpenContact}
toggleContactPanel={toggleContactPanel}
ref={panelRef}
/>
{children}
</main>
<Footer />
</>
);
}
the other components involved have only the navigation context imported from useContext and used. For example the footer:
import * as React from 'react';
//other imports...
import { GlobalSettingsContext } from '#contexts/GlobalSettings';
export default function Footer(): React.ReactElement {
const { navigation } = React.useContext(GlobalSettingsContext);
return (
<footer
className={
isSingleVehiclePage
? `${styles.footer} ${styles.morePadding}`
: styles.footer
}
>
<LayoutContainer>
<div className={styles.secondary}>
<div className={styles.social}>
{navigation.footerSocialIcon &&
navigation.footerSocialIcon.map((el: any, mainKey: number) => (
<Link key={mainKey} to={el.titleLink ?? ''}>
<span className={`icon-${el.iconClass ?? ''}`}></span>
</Link>
))}
</div>
</div>
</LayoutContainer>
</footer>
);
}
From the provided description i assume you are using getStaticProps. (although the it would be basically the same issue for getServerSideProps)
In order for data fetched to be pre-rendered in html, you need to pass it as props, returning it from getStaticProps.\
What you are doing is passing data to a react context, which is rendered after hydration takes place.
I advise you to review the basics of nextjs to understand what code is executed in the client and which is executed in the server, along with how pre-rendering works.
please check https://nextjs.org/docs/basic-features/pages

<Route > component alternative in nextjs

I have 3 components in nextjs and i want to achieve the below snippet in nextjs
<Route path="/" component={homePage} />
<Route path="/about" component={aboutPage} />
<Route path="/faq" component={faqPage} />
Q1. How can i do the same in nextjs without page refresh? (without react-router)
(Edit : some scholars are suggesting to read the docs but i have read it thoroughly and what i want is to pass a component along with the route)
Is this even possible in next js?
Q2: If i have url as /products?product_id=productid and on refresh if i want the url to be /products (basically i want to remove all params on refresh) What is the best practice to do this?
Thanks in advance
NextJS functions on a convention-based filesystem-based routing. You'd need to place your components in a directory structure that matches the routes you are wanting.
More details here:
https://nextjs.org/docs/routing/introduction
The Next.js docs don't really cover how to change away from <Route> components, however they have a lot of examples as code on how to do most things with Next.js. https://github.com/vercel/next.js/tree/canary/examples/layout-component
The below is what I used as an alternative to the component (there's no direct Next.js alternative).
_app.js
export default function MyApp({ Component, pageProps }) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout || ((page) => page)
return getLayout(<Component {...pageProps} />)
}
Any page:
import Layout from '../components/layout'
import Sidebar from '../components/sidebar'
export default function About() {
return (
<section>
<h2>Layout Example (About)</h2>
<p>
This example adds a property <code>getLayout</code> to your page,
allowing you to return a React component for the layout. This allows you
to define the layout on a per-page basis. Since we're returning a
function, we can have complex nested layouts if desired.
</p>
<p>
When navigating between pages, we want to persist page state (input
values, scroll position, etc) for a Single-Page Application (SPA)
experience.
</p>
<p>
This layout pattern will allow for state persistence because the React
component tree is persisted between page transitions. To preserve state,
we need to prevent the React component tree from being discarded between
page transitions.
</p>
<h3>Try It Out</h3>
<p>
To visualize this, try tying in the search input in the{' '}
<code>Sidebar</code> and then changing routes. You'll notice the input
state is persisted.
</p>
</section>
)
}
About.getLayout = function getLayout(page) {
return (
<Layout>
<Sidebar />
{page}
</Layout>
)
}
The main part for the layout that you want to wrap around the pages, components/layout.js:
import Head from 'next/head'
import styles from './layout.module.css'
export default function Layout({ children }) {
return (
<>
<Head>
<title>Layouts Example</title>
</Head>
<main className={styles.main}>{children}</main>
</>
)
}
What's happening is the _app.js wraps all pages inside the declared layout. Each page then defines what layout that page belongs to. The layout then accepts a page as the {children} prop object of which you can then render anywhere in your layout page.
Next uses filesystem based routing, your folder structure should look like
-- pages
-- index.js
-- about/index.js
-- faq/index.js
For the custom component part, make a component that's clickable, on click, use next builtin router to redirect
const router = useRouter();
router.push('/');

React Router - Empty Page, page not rendering

I am using React. I am using class Components.
I am also using React Router.
Here is my code:
constructor(props){
super(props);
}
handleCreateComponent = () =>{
return this.props.history.push('/app/pages/pageA/new');
}
render = () =>{
return(
<div>
<div>
<Button id="addComponent" onClick={this.handleCreateComponent}>ButtonA</Button>
</div>
</div>
);
}
My Question:
When I click "ButtonA", it navigates to the link: /app/pages/pageA/new
However, the page is empty and not displaying anything even though it's supposed to render.
What might be the problem ?
You should use <Link to="/app/pages/pageA/new"/> or <NavLink to="/app/pages/pageA/new"/> from the react-router instead of <Button/>
But the rendering problem might be something else. in your question you say /api/pages/pageA/new but in the code it's '/app/pages/pageA/new'

How to add both a header.html and a footer.html to all your routes when using singlepage?

We currently have a header.html and a footer.html that we include in our base.html that is loaded in the / route of our website. We're using a single page design and thus we don't reload base.html at any time other than when initially loading the page. In base.html we have a div with an id that we use to load all our html content with jQuery to. The problem we're facing now is that fact that accessing a route directly bypasses the load of header, and thus our jQuesry-scripts and bootstrap etc., and then the page looks like crap and doesn't work properly.
We figured we could remove the visual URL-change completely and use madeup route-names in our routes instead. But that feels like an overly complicated and not neccessarily secure way of doing it.
Ideally we would like to be able to know if the request is not coming from where it's supposed to.
Any ideas of how this can be accomplished? Or do you generally just expect no one to go to /signup on their own?
I use a Nav component which renders the "chrome" around the page.
export class Nav extends Component {
render = () => {
return (
<div>
<rb.Navbar>
<rb.Navbar.Header>
<rb.Navbar.Brand>
aktai
</rb.Navbar.Brand>
</rb.Navbar.Header>
</rb.Navbar>
{this.props.children}
</div>)
}
}
I mount it to all routes in React Router:
const router = (
<Router history={browserHistory}>
<Route component={Nav}>
<Route path="/folders" component={Folders}/>
</Route>
</Router>
)
You can also call it like:
<Nav>
<h1>welcome to my site</h1>
This is a place
</Nav>
Any component which renders this.props.children will work like that.

Categories