Home > Mobile >  Detect Function If Being Called In Another Component in React
Detect Function If Being Called In Another Component in React

Time:11-05

I have this function called handleSelectProduct in ProductComponent and I wanted to detect it in ProductDetailsComponent. If handleSelectProduct is called in ProductComponent, then I want to run a certain function in ProductDetailsComponent using useEffect.

ProductComponent

const ProductComponent = () => {
  const [selectedProduct, setProduct] = useState(null);

  const handleSelectProduct = (event) => {
    setProduct(event.target.value);
  };

  return (
    <div>
      <Select
        value={selectedProduct}
        onChange={(e) => handleSelectProduct(e)}
      >
        {(products || []).map(({ id, name }) => (
          <MenuItem value={id}>{name}</MenuItem>
        ))}
      </Select>
      <ProductDetailsComponent handleSelectProduct={handleSelectProduct} />
    </div>
  );
};

export default ProductComponent;

ProductDetailsComponent

const ProductDetailsComponent = ({ handleSelectProduct }) => {
  useEffect(() => {
    handleSelectProduct ? formik.setFieldValue("productInfo", "") : null;
  }, [handleSelectProduct]);
};

export default ProductDetailsComponent;

CodePudding user response:

If handleSelectProduct is called in ProductComponent, then I want to run a certain function in ProductDetailsComponent

I don't think that's exactly what you really want. If the components were that tightly coupled, you should use just a single component. The ProductDetailsComponent behaviour should depend only on its props and its internal state.

What you really want is to call a certain function in ProductDetailsComponent whenever the selected product changes. You can do that, as you already noticed, using useEffect - but with the product as a dependency. You just have to pass the selectedProduct as a prop to the component instead of the handleSelectProduct function.

const ProductComponent = () => {
  const [selectedProduct, setProduct] = useState(null);

  return (
    …
    <ProductDetailsComponent selectedProduct={selectedProduct} />
  );
};

const ProductDetailsComponent = ({ selectedProduct }) => {
  useEffect(() => {
    if (selectedProduct) formik.setFieldValue("productInfo", "");
  }, [selectedProduct]);
};

CodePudding user response:

If the child component wants to know if/when a function in the parent component was called then the parent should pass down a prop to the child informing it of the condition. You may need to also pass a function to the child for the child to "acknowledge" it "saw" the function was called so the parent can "reset".

const ProductComponent = () => {
  const [triggered, setTriggered] = React.useState(false);

  const handleSelectProduct = (event) => {
    setTriggered(true);
  };

  const reset = () => {
    console.log("Parent trigger was reset");
    setTriggered(false);
  };

  return (
    <div>
      Parent
      <button disabled={triggered} type="button" onClick={handleSelectProduct}>
        Trigger?
      </button>
      <ProductDetailsComponent triggered={triggered} reset={reset} />
    </div>
  );
};

const ProductDetailsComponent = ({ triggered, reset }) => {
  React.useEffect(() => {
    if (triggered) {
      console.log("Child saw fn triggered in parent");
      setTimeout(reset, 1000);
    }
  }, [reset, triggered]);

  return <div>Child</div>;
};

ReactDOM.render(
  <ProductComponent />,
  document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

An alternative could also be a simple function invocation count that is incremented, then the child need only see the count was incremented.

const ProductComponent = () => {
  const [triggered, setTriggered] = React.useState(0);

  const handleSelectProduct = (event) => {
    setTriggered(c => c   1);
  };

  return (
    <div>
      Parent
      <button type="button" onClick={handleSelectProduct}>
        Trigger?
      </button>
      <ProductDetailsComponent triggered={triggered} />
    </div>
  );
};

const ProductDetailsComponent = ({ triggered }) => {
  React.useEffect(() => {
    if (triggered) {
      console.log("Child saw fn triggered in parent");
    }
  }, [triggered]);

  return <div>Child</div>;
};

ReactDOM.render(
  <ProductComponent />,
  document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="root" />
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>

  • Related