Create class/component from string - javascript

Say i have a file like below on the server eg Github. I want to download the file as string with fetch and then be able to convert it to an object that i could use.
OBS: all import and the interface exist on locally so there wont be any thing missing.
import parser from '../interface/parsers';
import '../interface/extension';
export default class test implements parser {
public name: string;
constructor() {
}
}

Here's an example that at least works in a Snack (ignored the fetch part and just used a local string):
export default function App() {
const [evaledComponent, setEvaledComponent] = useState(null)
useEffect(() => {
this.React = React;
this.Text = Text;
setEvaledComponent(eval('this.React.createElement(this.Text, {}, "test component")'));
},[])
return <Text>{evaledComponent}</Text>
}
Note: I took a trick from from Why eval() is not working in React Native? assigning React to this.React in order to get it usable -- it's not a pretty hack and I'd recommend a more stable solution if you went ahead with this plan.
But:
This is an extremely uphill battle to take on and using eval is a terrible security risk since it executes arbitrary code. If there's another route you can take (like rendering things based on a JSON description), I would take that route instead.
Update:
Example of returning a custom object (non-React)
eval("var MyParser = { getString: () => {return 'hi there'} }");
return <Text>{MyParser.getString()}</Text>

Related

React server side component - alternative to `response.readRoot()` function

In React server components official GitHub example repo at exactly in this line here they are using response.readRoot().
I want to create a similar app for testing something with RSC's and it seems like the response does not contain the .readRoot() function any more (because they have updated that API in the react package on npm and I cannot find anything about it!). but it returns the tree in value property like below:
This means that whatever I render in my root server component, will not appear in the browser if I render that variable (JSON.parse(value) || not parsed) inside of my app context provider.
How can I render this?
Basically, if you get some response on the client side (in react server components) you have to render that response in the browser which has the new state from server but since I don't have access to readRoot() any more from response, what would be the alternative for it to use?
I used a trick o solve this issue, but one thing to keep in mind is that they are still unstable APIs that react uses and it's still recommended not to use React server component in the production level, uses it for learning and test it and get yourself familiar with it, so back to solution:
My experience was I had a lot of problems with caching layer they are using in their depo app. I just removed it. My suggestion is to not use it for now until those functions and APIs become stable. So I Removed it in my useServerResponse(...) function, which in here I renamed it to getServerResponse(...) because of the hook I created later in order to convert the promise into actual renderable response, so what I did was:
export async function getServerResponse(location) {
const key = JSON.stringify(location);
// const cache = unstable_getCacheForType(createResponseCache);
// let response = cache.get(key);
// if (response) return response;
let response = await createFromFetch(
fetch("/react?location=" + encodeURIComponent(key))
);
// cache.set(key, response);
return response;
}
and then creating a hook that would get the promise from the above function, and return an actual renderable result for me:
export function _useServerResponse(appState) {
const [tree, setTree] = useState(null);
useEffect(() => {
getServerResponse(appState).then((res) => {
setTree(res);
});
}, [appState]);
return { tree };
}
and finally in my AppContextProvider, I used that hook to get the react server component tree and use that rendered tree as child of my global context provider in client-side like below:
import { _useServerResponse } from ".../location/of/your/hook";
export default function AppContextProvider() {
const [appState, setAppState] = useState({
...someAppStateHere
});
const { tree } = _useServerResponse(appState);
return (
<AppContext.Provider value={{ appState, setAppState }}>
{tree}
</AppContext.Provider>
);
}
I know that this is like a workaround hacky solution, but it worked fine in my case, and seems like until we get stable APIs with proper official documentation about RSCs, it's a working solution for me at least!

Private methods in React service function component

I'm a bit new to React and I'm currently developing a Proyect which has a service layer. To do so, I created a function component which would have the methods as variables:
const CustomComponent = () => {
method1 = () => {...},
method2 = () => {...},
method3 = () => {...}
}
export default CustomComponent;
This component would then be imported to the component that will use it.
To make my architecture as clean as possible, I wanted to make some of the methods private. However, as you may already know, that is not possible to do in the solution I proposed. Do hoy have an idea on how to achieve this, or maybe there is a convention to make a service layer I'm not aware of?
Thank you so much in advance!
The architecture which I find particularly clean and maintainable is one where you split off logic from presentation into two files like this:
Service layer (ts):
export class Service implements ServiceInterface {
constructor(private instanceVariable: string = "foo") { }
private methodOne(): string {
return this.instanceVariable
}
public methodTwo(argumentVariable: string): string {
const importantString = this.methodOne();
return importantString + argumentVariable;
}
}
interface ServiceInterface {
methodTwo(argumentVariable: string): string;
}
export default new Service();
Service layer (js):
export class Service {
instanceVariable;
constructor(contructorArgument) {
this.instanceVariable = contructorArgument;
}
methodOne() {
return this.instanceVariable
}
methodTwo(argumentVariable) {
const importantString = this.methodOne();
return importantString + argumentVariable;
}
}
export default new Service();
Presentation layer:
import Service from "./service.ts";
const FunctionalComponent = () => {
const [localState, setLocalState] = useState(localStateInit);
return (
<>
<div>{Service.methodTwo("bar")}</div>
</>
)
}
Few things happen here (mostly regarding ts implementation).
Keep component's service and presentation layers in separate files.
Use an interface to describe the service class and its methods. This will help to work with your service layer in your component as you'll get Typescript's IntelliSense.
For this example I'm exporting an instance of the service as default export from its file. This gives you a cleaner API in your component's file, where you can call methods without having to "pollute" component file with instance creation. This has at least the following two drawbacks:
you mostly lose ability to work nicely with static class members
preconfigured instance variable (initiated as a private member in constructor) means its value cannot be replaced in testing.
If any of above are a no go, then clean up the constructor, export just the class itself and instantiate it as required in component file.
I'm also exporting the class itself. This is for testing purposes. In testing you want to be able to swap out arguments passed into class' constructor and you need to have class definition to do that.
You'll notice the shorthand notation for declaring and instantiating a private class variable in the constructor: private instanceVariable: string = "foo". This is equivalent to something like this:
class Service {
private instanceVariable: string;
constructor(constructorArgument: string) {
this.instanceVariable = constructorArgument;
}
Such notation is particularly nice when used with dependency injection.
Overall, this setup will help you with unit testing logic in your service layer, as you can test it like any other class. This comes particularly handy when there's a lot of conditional rendering logic.
Let me know if this is what you've been looking for. Maybe we could tailor it better for your use case.

How to declare array from API in place of hardcoded array?

I was given an example for some code that has an array hardcoded. Im looking to swap this out for my array that is pulled in from an API using graphql. Below is the code pen to the original example & another for what i've tried with no avail.
I'm pretty new to graphql & js so likely an amateur mistake, any pointers would be much appreciated!
Original code - https://codesandbox.io/s/nice-saha-gwbwv
My pen - https://codesandbox.io/s/quiet-wind-brq8s?file=/src/App.js
I would change your component structure to something like:
import React, { useState } from 'react'
import { graphql } from 'gatsby'
const YourPage = ({data}) => {
console.log('data is', data)
const [filters, setFilters] = useState({
type: "",
category: ""
});
//your calculations
return (
<div>
Your stuff
</div>
)
}
export const query = graphql`
query yourQueryName{
allStrapiHomes {
nodes {
type
category
}
}
}
`
export default YourPage
In your code, upon some critical imports, you are missing a few stuff from Gatsby. If you use a staticQuery, you will need to add a render mode to it. It may look a bit old-fashioned, it's better to use the useStaticQuery hook provided by Gatsby or adding a page query (my approach).
I've added a page query. Your data is under props.data.allStrapiHomes.nodes, destructuring props you omit the first step, so your data will be at data.allStrapiHomes.nodes. Both type and category will be an array if they are set like this in the Strapi back-end.

How can I get cookie from browser in Next js (without use getServerSide or getInitialProps)?

I want to get my authToken which located in
cookies.
But when I tried to get it, I got undefined or something like this.
I wanted to use useContext and put _app in the context(to check if the user is authorized in child elements), but I can't get the cookie in any way.
I can get cookie in following code that located in adminPanel element, but I don't want to break the DRY principle. Maybe you can recommend me some methods to get global context for auth? (without next-auth or something like
export async function getServerSideProps(ctx) {
if (ctx.req) {
axios.defaults.headers.get.Cookie = ctx.req.headers.cookie
return {
props: {
cookie: ctx.req.headers.cookie ? ctx.req.headers.cookie : ''
}
}
}
I tried to get at least something similar to the authorization token in _app, but nothing worked (i also tried getServerSideProps).
export async function getInitialProps(ctx) {
return {
props:{
ctx
}
}
}
In this case I got ctx: undefined...
Maybe you can recommend me some methods to get global context for auth? (without next-auth or something like that)
There's a lot to this question, so forgive me if I fail to answer directly, please let me know if you need further elaboration.
Firstly, there is a helper package for using cookies in next: https://www.npmjs.com/package/next-cookies
Secondly, if you want to keep your getServerSideProps DRY, you can always make a reusable function or a HOC wrapper and load it into whichever pages need it.
Thirdly, getting the context is slightly different for _app.jsx than it is to regular pages. The page-context is wrapped in an app-context, like so:
import App from "next/app";
class MyApp extends App {
//...
}
MyApp.getInitialProps = async (appCtx) => {
const appProps = await App.getInitialProps(appCtx);
const { ctx } = appCtx;
console.log(ctx);
// todo: get your data from ctx here
};
But beware, this will kill the static optimisation for your entire site, so it's best to keep it to just each page that needs it.

How to use the Ace editor validator without instantiating an Ace editor instance?

I use react-ace to create a CSS text editor in my React app.
That looks something like...
import Ace from 'react-ace'
...
<Ace
mode="css"
value={value}
onChange={onValueChange}
onValidate={onValidate}
...
/>
...
This works fine and dandy—highlighting CSS syntax errors and warnings. Also, the onValidate returns the error/warning "annotations" data structure.
However there is a need, elsewhere in the application, to run the same validator used in this React Ace component, but outside of the context of this Component. Essentially I need to pass the content in value through the error/warning annotation system, but can't instantiate this react element.
I've tried the following:
import { EditSession } from 'brace'; # "brace" is the "module" compatible version of the ace editor that our "react-ace" uses
import 'brace/mode/css';
export const getCssAnnotations = (value)=> {
const editSession = new EditSession(value);
editSession.setMode('ace/mode/css');
const annotations = editSession.getAnnotations();
return annotations;
};
However, the annotations returned by this function are always []! I assume this is because I'm just accessing the annotation setter/getter interface, and not actually running the annotations creator. But I can't figure out what actually does the annotations work normally.
I've looked at docs on Creating a Syntax Highlighter for Ace, but don't understand if/why a web worker would need to be involved here.
Thanks!
This doesn't work, because editSession uses web worker to generate annotations which is async:
editSession.on('changeAnnotation', () => {
let annotations = editSession.getAnnotations();
callback(null, annotations)
});
docs
Note that currently each editSession creates a new worker, so it is better to use setValue on an existing instance of editSession, or call editSession.destroy() before calling the callback
So a full solution might look like:
const getAnnotationsPromise = (value, mode)=> {
const editSession = new EditSession(value);
editSession.setMode(`ace/mode/${mode}`);
return new Promise((resolve)=> {
editSession.on('changeAnnotation', () => {
const annotations = editSession.getAnnotations();
editSession.removeAllListeners('changeAnnotation');
editSession.destroy();
resolve(annotations);
});
});
};

Categories