Gatsby XSS Prevention - reactjs

I've read that React only prevents XSS in children and not props. Is this code an XSS vulnerability?
import React from "react"
import { graphql } from "gatsby"import Layout from "../components/layout"
export default ({ data }) => ( <Layout>
<h1>About {data.site.siteMetadata.title}</h1>
<p>{data.body}</p>
<img src={data.url} />
</Layout>
)
Should I be using children and call the component like this?
(data) => <Component>
<h1>About {data.site.siteMetadata.title}</h1>
<p>{data.body}</p>
<img src={data.url} />
</Component>
as opposed to:
(data) => <Component {data} />

That snippet isn't XSS vulnerable, you could check it out by yourself.
// import React and others ...
function MyComponent({ inject, children }) {
return (
<main>
<div>{inject}</div>
<div>{children}</div>
</main>
);
}
function App() {
return (
<MyComponent inject="<script>console.log('Executed from prop')</script>">
{'<script>console.log("Executed from children")</script>'}
</MyComponent>
);
}
Script tags are rendered as string nodes. The only way you have to bypass that is by using prop dangerouslySetInnerHTML, that way you get XSS vulnerable, hence the name.
I hope it helps!

Related

React JSX to string

How can I convert the children of a React component to a string without it rendering?
import { renderToString } from 'react-dom/server';
const MyComponent = () => {
return (
<h1>Hello</h1>
)
}
const Output = () => {
return (
<pre>
{renderToString(
<div>
<MyComponent />
<MyComponent />
<MyComponent />
</div>
}
</pre>
)
}
How can I make the above show:
<div>
<MyComponent />
<MyComponent />
<MyComponent />
</div>
instead of
<div>
<h1>Hello</h1>
<h1>Hello</h1>
<h1>Hello</h1>
</div>
Ultimately I'm looking to show how the code for the component is written in a ui system. Trying to parse the children instead of writing the component twice. Any examples of open source websites demonstration ui components with a code block works too, and I'll just look into that.
Take a look at react-code-blocks. Basically you can add any code, just like Stackoverflow.

rendering component, after another distant component renders

In navigation menu app, down the component tree, there is a dropdown menu component DropdownMenu2, with menu items, which are <NavLinks> components. Every time an item is clicked, it points to one of the <Route>s in main App. Every <Route> is a page, containing Infofield component. So every time <NavLink> is clicked, Infofield is rendered.
My puzzle is: I need the HeaderLogo component be rendered, everytime Infofield is rendered (HeaderLogo contains animation). I failed when constructing useEffect hook in Infofield. That hook was intended to contain custom hook, producing a variable with changing state. That hook could be then lifted up to App, from there variable would be passed to HeaderLogo, inline to the key property. If that idea is legit, I'm experiencing difficulties with construction of custom hook inside of useEffect. Maybe (probably) there is a better way...
Apps most basic structure looks like this:
App
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import HeaderLogo from "./components/HeaderLogo";
import NaviMain from "./components/NaviMain";
import Info from "./pages/Info";
/...
import { UserContext } from "./components/sub-components/UserContext";
function App() {
return (
<Router>
<div className="App">
<HeaderLogo />
<NaviMain />
<Routes>
<Route path="/Info" element={<Info />} />
/...
</Routes>
</div>
</Router>
);
}
export default App;
NaviMain
import "./NaviMain.css";
import NaviMainButton from "./NaviMainButton";
import NaviMainButtonDrop2 from "./NaviMainButtonDrop";
const NaviMain = () => {
return (
<nav>
<ul>
<NaviMainButtonDrop2 />
</ul>
</nav>
)
}
export default NaviMain
NaviMainButtonDrop2
import DropdownMenu2 from "./DropdownMenu2";
const NaviMainButtonDrop2 = () => {
return (
<li>
<a>
title
</a>
<DropdownMenu2 />
</li>
)
}
export default NaviMainButtonDrop2
DropdownMenu2
import "./DropdownMenu.css"
import { NavLink } from "react-router-dom";
import { MenuItemContentSchool } from "./sub-components/MenuItemContentSchool"
const DropdownMenu2 = () => {
return (
<div className=dropdown-holder-us>
{/* here menu unfolds */}
{MenuItemContentSchool.map((item) => {
return (
<NavLink
to={item.link}
className={(navData) => (navData.isActive ? "d-content-us active-style" : 'd-content-us')}
key={item.id}
>
{item.title}
</NavLink>
)
})}
</div>
)
}
export default DropdownMenu2
Info (one of the <Route>'s )
import InfoField from "../components/InfoField"
const Info = () => {
return (
<section className="intro-index">
<InfoField text={"welcome"} />
</section>
)
}
export default Info
HeaderLogo
import "./HeaderLogo.css";
const HeaderLogo = () => {
return (
<header>
<h1 className="head-main">learning curve</h1>
</header>
)
}
export default HeaderLogo
From what I can gather you simply want to "rerun" an animation in the HeaderLogo component when the path changes. Import and use the useLocation hook and use the pathname value as a React key on the header element with the animation to want to run when it mounts. The idea here is that when the React key changes, React will remount that element.
Example:
import { useLocation } from "react-router-dom";
import "./HeaderLogo.css";
const HeaderLogo = () => {
const { pathname } = useLocation();
return (
<header>
<h1 key={pathname} className="head-main">
learning curve
</h1>
</header>
);
};
export default HeaderLogo;
This is a classic job for a global state. You can declare a boolean state, i.e showHeader, and add conditional rendering to the tag.
The global state variable showHeader will be changed each time you click on a dropdown item, and in the App functional component you should listen for a change in this variable. (For example, using Redux, you'll use useSelector(state=>state.showHeader) in App.
For an example, this is the App component with conditional rendering for the HeaderLogo. In order for this to be useable, you need to build a Redux store and reducer functions. Read the official Redux docs for more
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import { useSelector } from 'react-redux';
import HeaderLogo from "./components/HeaderLogo";
import NaviMain from "./components/NaviMain";
import Info from "./pages/Info";
/...
import { UserContext } from "./components/sub-components/UserContext";
function App() {
const showHeader = useSelector(state=>state.showHeader)
return (
<Router>
<div className="App">
{showHeader ? <HeaderLogo /> : null}
<NaviMain />
<Routes>
<Route path="/Info" element={<Info />} />
/...
</Routes>
</div>
</Router>
);
}
export default App;
</Router>

How to call a state from a component to a page?

I am creating an example dApp which carries the "Header" component at the top of the page for every page. So, I have created a header component and I make people connect to their MetaMask wallet in Header.tsx, which they do successfully and I keep their wallet ID with currentAccount state.
Header.tsx:
const Header: FunctionComponent<{}> = (props) => {
const [currentAccount, setCurrentAccount] = useState("");
async function checkAccount() {
const accounts = await window.ethereum.request({ method: 'eth_requestAccounts' })
setCurrentAccount(accounts[0]);
}
return (
<header>
<div className="col-xl-3 col-lg-3 col-md-3 col-sm-3">
<ul>
<li>{!connectHidden && <button className="buy connect-wallet" onClick={connectWallet}><b>Connect Wallet</b></button>}</li>
</ul>{currentAccount}
<ul>
<li>{!disconnectHidden && <button className="buy connect-wallet" onClick={disconnectWallet}><b>Disconnect Wallet</b></button>}</li>
</ul>
</div>
</header>
);
};
export default Header;
But at my homepage, there are no codes for anything about getting user's wallet ID, I don't want to rewrite the code here as it is not the right way. As a newbie in react, I couldn't make the codes I have tried work like trying to import a function or variables. How do I call the currentAccount state in my home page?
Home.tsx:
const HomePage: FunctionComponent<{}> = () => {
useEffect(() => {
onInit()
return () => { }
}, [])
async function onInit() {
}
async function onClickMint() {
alert("mint");
}
return (
<>
<div>xx
</div>
</>
);
};
export default HomePage;
Here is my app.tsx and as you can see, I am seeing all of the components at once. But I want to use the state I have got at Header component in my Home component.
App.tsx:
import Header from './components/Header';
const App: FunctionComponent<{}> = () => {
return (
<Router>
<Header />
<Route exact path="/" component={HomePage} />
<Route exact path="/wallet" component={Wallet} />
<Footer />
</Router>
);
};
export default App;
Quick answer:
simply create your state at the top level (App.tsx) and give currentAccount, setCurrentAccount as props for the other components
App.tsx:
import Header from './components/Header';
const App: FunctionComponent<{}> = () => {
const [currentAccount, setCurrentAccount] = useState("");
return (
<Router>
<Header />
<Route exact path="/">
<HomePage currentAccount={currentAccount} setCurrentAccount={setCurrentAccount}/>
</Route>
<Route exact path="/wallet">
<Wallet currentAccount={currentAccount} setCurrentAccount={setCurrentAccount}/>
</Route>
<Footer />
</Router>
);
};
export default App;
Longer answer:
You need to inform yourself about redux or simply the useContext hook
For instance with the useContext hook you can create a context that will contain your state and that you will be able to access in any child component without using props which can be redundant when you have multiple children and grandchildren ...
Here you can find the documentation about how to use the useContext Hook :
https://reactjs.org/docs/hooks-reference.html#usecontext

How to make a lazy loading high order component in React

So here is the main idea, HOC that is be able to load any wrapped in component with React.lazy and React.Suspense. Is it possible???
So, I already was able to write some, but not sure that I was able to made properly...
import React, { Suspense, lazy, useState, useEffect } from "react"
export function withLazyImport(LazyComponent) {
return (props) => {
const [loadedComponent, setLoadedComponent] = useState(null)
useEffect(() => {
setLoadedComponent(lazy(() => import(<LazyComponent {...props} />)))
//eslint-disable-next-line
}, [])
return (
<Suspense fallback="Lazy component is loading ...">
{loadedComponent}
</Suspense>
)
}
}
I don't understand why do you use useEffect. The resulted component will not pass new props to the lazy component because props are passed on did mount.
I came up with another solution based on the example provided by the author of this question
import React, { Suspense } from 'react';
export const withLazyComponent = (LazyComponent) => {
return (props) => (
<Suspense fallback="Lazy component is loading ...">
<LazyComponent {...props} />
</Suspense>
)
}
And then you use it like:
const LazyComponent = withLazyComponent(React.lazy(() => import('path/to/component')));
Here's how to achieve it in TypeScript
import { Loader } from "../components/loader";
import { Suspense } from "react";
/**
* HOC to wrap a component in a Suspense component.
*/
export default function withSuspense<P>(Component: React.ComponentType & any) {
return function WithSuspense(props: P) {
return (
<Suspense fallback={<Loader />}>
<Component {...props} />
</Suspense>
);
};
}
you can try using already existing solutions like Loadable Components

How can I use shared components between routes in Next.js

I'm thinking of migrating from create-react-app to Next.js
I need to share component between routes in Next.js
I tried the Layout file example below and it worked pretty well for me but I have a special case where I need the shared component to be above the router itself.
For example, if I have a video element and want the video to still playing if I changed the route
const Layout = (props) => (
<div>
{props.children}
<video width="400" controls>
<source src="https://www.w3schools.com/html/mov_bbb.mp4" type="video/mp4"/>
Your browser does not support HTML5 video.
</video>
</div>
)
const Home = () => (
<Layout>
<Link href="/about"><a>About</a></Link>
<h1 className='title'>Welcome to Home!</h1>
</Layout>
)
const About = () => (
<Layout>
<Link href="/"><a>Home</a></Link>
<h1 className='title'>Welcome to About!</h1>
</Layout>
)
Example from another project using create-react-app and reach-router
the player component is above the router
<Layout>
<Player/>
<Router className={'router'}>
<Login path={Routes.login}/>
<NotFound404 default/>
<Home path={Routes.root}/>
<Test path={Routes.test}/>
</Router>
<Layout/>
The shared element will be the video tag, the problem is on every route change the video is rendered and replayed
You might wanna check the docs on using pages/_app.js for that.
Example usage is shown below:
import Head from "next/head";
type Props = {
pageProps: any;
Component: React.FC;
};
const App = ({ Component, pageProps }: Props) => {
return (
<>
<Head>
// You can put whatever you want to put in your document head
</Head>
<YourSharedComponent />
<Component {...pageProps} />
<style>{`
// styles...
`}</style>
</>
);
};
App.getInitialProps = async ({ Component }) => {
const pageProps = Component.getInitialProps && await Component.getInitialProps(ctx, loggedUser);
return {
pageProps,
}
};
export default App;

Resources