Search code examples
javascriptreactjsscopeevalscoping

React - eval'ing event handlers within child component's scope


My app contains two components: 'Parent' and 'Child':

import React from "react";
import ReactDOM from "react-dom";

function Parent() {
  const t = "Parent";
  return (
    <div className="Parent">
      <Child
        content={
          <button type="button" onClick={() => eval("console.log(t);")}>
            Show t
          </button>
        }
      />
    </div>
  );
}

function Child(props) {
  const t = "Child";
  return props.content;
}

const rootElement = document.getElementById("root");
ReactDOM.render(
  <React.StrictMode>
    <Parent />
  </React.StrictMode>,
  rootElement
);

Link to Sandbox

Parent passes some content to Child and Child renders it. In my example, the content is a button. This button has an onClick handler that prints the value of some variable t when it is clicked. For reasons beyond my control, the handler uses eval and is evaluated within the Parent component's scope (if you click the button, you can see that it logs "Parent", which is the value of t inside the Parent component). However, I need to scope t within the Child's scope (so that it logs "Child" when the button is clicked). Is there a way to do this?

I'm not very clear on JS scoping rules but as I understand it, eval has local scope when called directly. But Child's content is not known to me beforehand so I can't call it inside Child itself. Also, I cannot avoid using eval altogether since the handler will always be passed to the component as a string.


EDIT: I can use React.cloneElement to wrap the handler in eval inside the Child component like so:

function Child(props) {
  const t = "Child";
  return React.cloneElement(props.content, {onClick: eval(props.content.props.onClick)});
}

This works but is untenable if content has nested components - I would have to recurse through them and it gets messy.


Solution

  • Could you make a content to be a function of variable t and then call it inside the component with scope of which you want to associate that variable?
    I mean something like below code:

    function Parent() {
      const t = "Parent";
      return (
        <div className="Parent">
          <Child
            content={(
               t // <-------------------- note that it is a function now
            ) => (
              <button type="button" onClick={() => eval("console.log(t);")}>
                Show t
              </button>
            )}
          />
        </div>
      );
    }
    
    function Child(props) {
      const t = "Child";
      return props.content(t); // <--------------------- calling it here, so it see t from child component
    }
    

    EDIT: I can use React.cloneElement to wrap the handler in eval inside the Child component This works but is untenable if content has nested components - I would have to recurse >through them and it gets messy.

    It also should not cause problem with recursion, because variable t, that you would pass to that function will be in scope of that function, so all components created there should see it ( like it is happening now with your Child component, which see variable from Parent's scope.)