Search code examples
reactjsd3.jsevalobservable-plot

Plot is not defined when using it in eval()


I have a notebook project that uses React and ObservablePlot and react-monaco
there's an IDE on my App that :

  1. takes the User Code ( Observable Code ) like this Plot.lineY(props.data, { x: "Date", y: "Close" })

  2. then I pass that code as a prop to my Component

  3. then I pass that prop string code to the eval

the error that I faced with

Plot is not defined ReferenceError: Plot is not defined


What I want:

is to Compile that code with eval and put the return value to marks of the Observable plot.

OR

somehow convert user string code to JS readable code


Plot is an npm package so i imported it from its package like below

Here is my Code:

import React, { useEffect, useState, useRef } from "react";
import * as Plot from "@observablehq/plot";
import * as d3 from "d3";

const PlotChart = (props) => {
  const containerRef = useRef();
  const [data, setData] = useState();

  useEffect(() => {
    d3.csv("/gistemp.csv", d3.autoType).then(setData);
  }, []);

  useEffect(() => {
    if (data === undefined) return;
    const plot = Plot.plot({
      marks: [eval(props.plot)],
    });
    containerRef.current.append(plot);
    return () => plot?.remove();
  }, [data]);

  return <div ref={containerRef} />;
};

export default PlotChart;

I searched a lot and I got no answer for this Problem. Any guide will be appreciate


Solution

  • Hey guys for anyone who might face with same problem.

    It looks like the eval can't use the imported Package; instead, you can store it in a variable that is declared in the same scope that the eval gets used. then use that variable name in the string code that you want to pass into eval.

    const PlotChart = (props) => {
     const containerRef = useRef();
     const [data, setData] = useState();
     
     const compileCode = () => {
        // store package in a variable
        const plotly = Plot;
        return eval(props.plot);
      };
    
      useEffect(() => {
        compileCode();
        setData(props.data);
      }, []);
    
      useEffect(() => {
        if (data === undefined) return;
        const plot = Plot.plot({
          marks: [compileCode()],
        });
        containerRef.current.append(plot);
        return () => plot?.remove();
      }, [data]);
    
      return <div ref={containerRef} />;
    };
    
    export default PlotChart;
    

    now if you pass a string like below your eval will work fine.

    plotly.lineY(props.data, {x: 'Date', y: 'Close'})