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 inProductComponent
, then I want to run a certain function inProductDetailsComponent
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>