craft.js icon indicating copy to clipboard operation
craft.js copied to clipboard

Why can't we pass local state Function from parent to child component using `<Element/>` ?

Open ProPhycient5 opened this issue 2 years ago • 1 comments

While creating a component component like Dropdown, there is use case where we define local useState variable and its func in the parent component and want to pass it as prop to its child component, which we achieve easily in normal React Component.

export const DropDown = () => {

  const { connectors: { connect },} = useNode();

  const {enabled} = useEditor((state) => ({ enabled: state.options.enabled,}));

  const [isOpen, setIsOpen] = useState(false);

  const toggleClick = () => {
    if (!enabled) {
      setIsOpen(!isOpen);
    }
  };


  return (
    <div className={props.className} ref={connect} onClick={toggleClick}>
      <Element canvas id="wo" is={DropDownHead}>
        <Text {...props.textComponent} text={props.text}/>
        <Text
          {...props.textComponent}
          className="material-icons"
          text={props.iconText}
        />
      </Element>
      {isOpen && (
        <Element canvas id="xv" is={DropDownContent} >
        </Element>
      )}
    </div>
  );
};

The above is code for the Dropdown component, but there is a flaw in above code i.e. when click on <Content/> component, Dropdown will not show its content, which we don't want. We that control(or onClick eventListener) to be their in DropDownHead component.

 <Element canvas id="wo" is={DropDownHead} isOpen={isOpen} setIsOpen={setIsOpen}>

In above code, if we could pass local state variable and its Function as props to DropDownHead component. then we would create toggleClick function within DropDownHead component as below 👇🏼

export const DropDownHead = ({ children, isOpen, setIsOpen, ...props }) => {
 props = {
   ...dropDownHeadDefaultProps,
   ...props,
 };
 
 const toggleClick = () => {
   if (!enabled) {
     setIsOpen(!isOpen);
   }
 };

 const {
   connectors: { connect },
 } = useNode();

 return (
   <div ref={connect} className={props.className} onClick={toggleClick}>
     {children}
   </div>
 );
};

But I am only accessing isOpen not setIsOpen func,

Why is it so ? Then what will be the solution in order to achieve the above explained feature ????

ProPhycient5 avatar Mar 26 '22 16:03 ProPhycient5

When you use <Element /> - you're creating/referencing a Craft Node, and Nodes are serialisable. We don't currently allow functions to be props of Nodes since we can't serialise functions.

We could potentially allow a special way to pass these sort of props for Linked Nodes so that these props won't be added to the corresponding Node, but will just be passed over to the underlying React Component:

<Element id="..." is={Button} $onClick={...} /> 
// $onClick will be passed to the Button component, but won't exist in the Button Node

But will need to think about this more, since this will only work for Linked Nodes and not for the child Nodes.

In your example, if you really need it to work they way you want it to - you can either move the setOpen function to a parent element outside of the Linked Node or manually add an event listener to the <div> in the DropdownHead component from the parent component that will call setIsOpen

prevwong avatar Mar 28 '22 10:03 prevwong