How to evaluate a string as a React component? - javascript

I'm trying to make a website that lets users input some react code, then it renders it on the other side of the page, so they can see what it looks like.
My problem is, I have the user's source code as a string (which may return a function or class component), but I don't know how to convert that to an actual react component that can be rendered.
First I tried using the new Function() constructor, which lets you create a function from a string, which looks like this:
import {render} from "react-dom"
const userInputtedCode = `
return function App() {
return <div>Hello world</div>
}
`
const func = new Function("React", userInputtedCode);
const App = func(React)
render(<App/>, document.getElementById('WorkFlow'));
But this doesn't work, as I get the error SyntaxError: expected expression, got '<'
I have also tried libraries such as react-jsx-parser, but this doesn't fit what I need, as I want to make an entire react component which may contain state, props, nested components, etc, not just parse some JSX.
Any ideas of how I can convert strings of source code that return a function/class into actual react components? Thanks!

You can try this approach:
import React, { Component } from "react";
import { render } from "react-dom";
import * as babel from "babel-standalone";
const userInputtedCode = `
function App() {
return <div>Hello world</div>
}
`;
const babelCode = babel.transform(userInputtedCode, {
presets: ["react", "es2017"]
}).code;
const code = babelCode.replace('"use strict";', "").trim();
const func = new Function("React", `return ${code}`);
const App = func(React);
render(<App />, document.getElementById("root"));
PS: Make sure to run npm i babel-standalone before running the app.

Related

Retrieve Route Parameters in any Component

Suppose my URL looks something like this:
/blog/[post_id]/something
What is the recommended way to pass $post_id down to any component anywhere in the tree?
I know how to retrieve route parameters using getInitialProps but passing the values down is always giving me a hard time.
For pages I could technically use React Contexts although this seems a bit oversized for such a trivial use case.
For layouts I am honestly completely lost because pages are children of layouts and the return value of getInitialProps is passed to the page and not the layout.
My components could make use of useRouter but this requires useEffect and would also make my component depend on the route itself...
Any advice would be welcome (:
My components could make use of useRouter but this requires useEffect and would also make my component depend on the route itself...
useRouter seems like the obvious solution here. I'm not exactly understanding your concerns regarding the component depending on the route. I guess it does make the Layout less flexible since it needs to know that the post id is stored in the post_id query variable. But I would do it anyways :) It gives you a nice and simple way to access the query variables which can be used in a Layout that's outside of your BlogPost or in a deeply-nested component that you use inside the BlogPost.
Using the per-page layouts approach:
/components/Layout
import { useRouter } from "next/router";
import { ReactNode } from "react";
export default function Layout({ children }: { children: ReactNode }) {
const router = useRouter();
return (
<div>
<h3>You are viewing post id #{router.query.post_id}</h3>
{children}
</div>
);
}
/pages/blog/[post_id].jsx
import Layout from '../../components/Layout';
export default function BlogPost() {
return <div>Hello World</div>
}
BlogPost.getLayout = function getLayout(page) {
return (
<Layout>
{page}
</Layout>
)
}
/pages/_app.tsx (to support per-page layouts, copied from docs)
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} />)
}
I think the easiest and the cleanest way is to use window.location.pathname. this will give you the part after the domain name. for example for
http://localhost:3001/blog/[post_id]/something
you will get /blog/[post_id]/something
const pathname=window.location.pathname
const splittedPathname=pathname.split("/") // ['', 'blog', '[post_id]', 'something']
const dynamicId=splittedPathname[2]
you can run above code in useEffect and set a state. or you could write a hook and use it in the components that under dynamicId components
import React, { useState, useEffect } from "react";
const usePathname = () => {
const [postId, setPostId] = useState("");
useEffect(() => {
const pathname = window.location.pathname;
const splittedPathname = pathname.split("/");
const dynamicId = splittedPathname[2];
setPostId(dynamicId);
}, []);
return { postId };
};
export default usePathname;
If you are looking for client side rendering, useRouter is the best way to go. If you are looking for SSR or SSG, you should rather use getStaticProps or getServerSideProps.

Correctly create global variables in React

As title. I've searched about this problem on this site but I didn't find a solution for my case.
I've defined a global variables in public/static/js/A.js in my Visual Studio project:
var pVariable=new Class1();
There is a function LoadList() in Class1.
That variable will be used in several functions in my project, so I set as a global variable, and I included the JS file to public/index.html in the same project:
<script type="text/javascript" src="%PUBLIC_URL%/static/js/A.js"></script>
I use that variable in src/Clsb.js in the same project....
var alist=pVariable.LoadList();
export default class Clsb extends Component{
render(){
return (<Table dataSource={alist} />);
}
}
When I start debug in Visual Studio , I got an error:Failed to compile: 'pVariable' is not defined no-undef
But I am sure the JS file contains that variable is included. Could someone guide me to fix where I made it wrong?
You can do that by storing the variable in window variable
first store the value of your variable in A.js like this
window.pie = 3.1416;
And you can see the variable value in your Clsb.js component like
console.log(window.pie);
As phuzi said in the comment to your question, declaring global variables in react is a bad idea, in this case the ideal would be to use redux, or else the context api (context hook) so you can access and make the variable available throughout your system.
Link to docs
https://reactjs.org/docs/context.html
👇 context with functional component
let's create a context like hook
/src/context.js
import React, { createContext, useState, useContext} from "react";
const UserContext = createContext();
export default function UserProvider({ children }) {
//your variables
//example
const [person, setPerson] = useState('John');
return (
<UserContext.Provider
value={{
person //variables to export
}}
>
{children}
</UserContext.Provider>
);
}
export function useUser() {
const context = useContext(UserContext);
if (!context) throw new Error("useUser must be used within a CountProvider");
const { person } = context;
return { person };
}
after creating the context, around your app you need to place the provider
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
import UserProvider from "./src/context.js";
ReactDOM.render(
<UserProvider>
<React.StrictMode>
<App />
</React.StrictMode>
</UserProvider>,
document.getElementById("root")
);
after you already have the provider you can access any file
src/page.js
import React from "react";
import { useUser } from "./context.js";
const Page = (props) => {
const { getPerson } = useUser(); //variable available by context
return (<h1>Test h1</h1>)
};
export default Page ;
obs: i didn't test the code
Global variables is not a good practice in React. Whenever you find you find yourself needing that, it's most likely that what you want us instead Global state Management.
My recommendation is :
-first to try React's built in global state Management toolsn like Context API https://reactjs.org/docs/context.html
-Then if the first doesn't serve your need, try third party state Management libraries like redux from https://react-redux.js.org/introduction/ or mobx

Using external js library in functional component

I am using the CircleType.js library which allows you to curve a string of text/render it as a circle. However, I am very new to React and I'm not sure how to use the library in React. I created a functional component and used the documentation provided in the CircleType page to create... but I keep getting a 'TypeError: Cannot read property 'innerHTML' of null' error.
import React, {useEffect} from 'react'
import CircleType from 'circletype'
function RotatingCircle() {
// const circleOfSkills = new CircleType(document.getElementById('rounded-text'))
// .radius(100)
// useEffect(() => {
// return () => circleOfSkills.mount()
// }, [circleOfSkills])
return (
<p className="circle" id="rounded-text">
AND THE WORLD KEEPS GOING AROUND AND AROUND AND AROUND
</p>
)
}
export default RotatingCircle
I read that I might need to use refs but I'm really not sure how to use it as all examples I see use class components. Another forum I saw suggested using useEffect, but I'm clearly not using it correctly.
How do I reference DOM elements in a functional component?
Here is an example of CircleType implementation with React useRef hook. Avoid using getElementById for DOM manipulation as it is not the React way.
Sample code and CodeSandbox link:
import React, { useEffect, useRef } from "react";
import "./styles.css";
import CircleType from "circletype";
export default function App() {
const circleInstance = useRef();
useEffect(() => {
new CircleType(circleInstance.current).radius(100);
}, []);
return (
<div className="App">
<div ref={circleInstance}>abcdef</div>
</div>
);
}
CodeSandbox
try this
useEffect(() => {
const circleOfSkills = new CircleType(document.getElementById('rounded-text'))
.radius(100)
return circleOfSkills.mount()
}, []);
Try moving the const inside useEffect like this:
useEffect(() => {
const circleOfSkills = new CircleType(document.getElementById('rounded-text'))
.radius(100)
return () => circleOfSkills.mount()
}, []);
Calling getElementById outside of useEffect will give you null error because the element is not yet rendered on the page.
When using react I'd avoid using getElementbyID inside your components. Defining a root in your index.html and then linking it in index.js by
import React from "react";
import ReactDOM from "react-dom";
import App from "./App"; //App is your base react component
ReactDOM.render(
<App />
document.getElementById("root")
);
It will save you headaches like this and others in the future. React components are like a tree and by defining only one root you are utilizing what react was built for.

Convert website from HTML,CSS,bootstrap & JQuery to React?

Hi guys hope you're fine, I'm student and I have this year to do some thing like a project to end my studies , so I chose to create a website (using React/Django) I already have the site but made by HTML,CSS,bootstrap & JQuery , so now i have to convert it to react , but i have a problem i don't know how to include some js files inside a components , every things else is going good, I need just what is in the js files to applied it in some components.
Hope you help me out.
cordially
You can have javascript code inside your components likewise
const Component = props => {
//javascript code
return (<div>-- Component JSX---</div>)
}
if the javascript code if just needed for the initializing of the component you can use react hooks to run a piece of code only one time after the component is created.
import React, { useEffect } from 'react';
const Component = props => {
useEffect(() => {
// javascript code
}, [])
return (<div>--Component JSX---</div>
}
the empty array as second argument indicates the useEffect hook that the effect should only be ran once after the component has been initialized.
So the way React works is you will be building "HTML" using React functional/class components like this example
import React from 'react';
//Just like a normal javascript function, it listens to in this
instance, the return statement. You're returning regular HTML.
function Navbar() {
return (
<div>This is some text built by react</div>
<p>Saying hello to react by building a functional component</p>
)
}
export default Navbar; //This right here is exporting the file so it can be
//used elsewhere just import it in other file.
So the return is where you will build your website, then in the next component you will import should look something like this.
Normally, it is called App.js or in some instances where it's more complex it's anythinng you want.
import Navbar from '../components/Navbar.js';
function App() {
return (
<Navbar /> //Now you don't have to write your main content in here you can
//just import it. Just like Navbar
<div>This is my main content in page</div>
)
}

Using nodejs & react together (in electron)

I have a React component which I use in my electron application.
// #flow
import React, { Component } from 'react';
import Home from '../components/Home';
import SentenceView from '../components/SentenceView';
import Dictionary from '../components/Dictionary';
export default class HomePage extends Component {
render() {
const aTestArray = [1,2,3,4];
return (
<div>
<SentenceView numbers={aTestArray} />
</div>
);
}
}
I am giving my SentenceView component aTestArray. Now, in reality I won't have this array available but will need to create it myself. Since React is only the View, I do not want to create the array with React, but outsource this to a function I have written in nodejs. It creates an array and then returns it. So what (as of now) I have written as
const sentenceArray = ['Hello', 'Mister'];
would ideally look something like this then:
const sentenceArray = createTheArrayFunction(); //returns array
However, I do not know how I can make this work. I do not know how to give my React component access to function which is in a nodejs file, or how else I would connect the two. I am using electron, so it should be possible for me to use nodejs (somewhere somehow). But I just have no clue how to put these things together. I would be glad if someone could help me out.

Categories

Resources