Handle children.props - reactjs

so, in ParentComponent I have
<Component dropDownContent={<DropDownContent content={array} onSelect={handleSelect} />} />
my DropDownContent looks something like this
return (<ul>
{content.map((item) =>
{ return <li><button onClick={()=> onSelect(array.id)}>{array.name}</button></li>}
)}
</ul>)
Can I some how do something with the onSelect inside Component even if I add DropDownContent as a prop to Component?
Thanks :)

What I understand from your question is that you want to pass a function from the parent component to the child component. And when a local function inside child component is clicked, you want to call that passed function. if yes, then this is your solution:
Note: I do not know exactly what code you wrote and what your component consists of. So I will give you the answer by giving a simple example to fully understand the solution.
In your parent component:
export const ParentComponent = props => {
const handleSelect = () => console.log(`do something here`);
return (
<Component
dropDownContent={
<DropDownContent
content={array}
onSelect={() => handleSelect()}
/>}
/>
)
}
And in your child component you need to receive the passed function like below:
export const ChildComponent = props => {
const handlePassedFunction = () => props.onSelect?.();
return (<ul>
{content.map((item) => {
return <li>
<button onClick={() => handlePassedFunction(array.id)}>{array.name}</button>
</li>
}
)}
</ul>)
}

Related

Is it possible to pass components as props, or Images as props [duplicate]

Lets say I have:
import Statement from './Statement';
import SchoolDetails from './SchoolDetails';
import AuthorizedStaff from './AuthorizedStaff';
const MultiTab = () => (
<Tabs initialIndex={1} justify="start" className="tablisty">
<Tab title="First Title" className="home">
<Statement />
</Tab>
<Tab title="Second Title" className="check">
<SchoolDetails />
</Tab>
<Tab title="Third Title" className="staff">
<AuthorizedStaff />
</Tab>
</Tabs>
);
Inside the Tabs component, this.props has the properties
+Children[3]
className="tablist"
justify="start"
Children[0] (this.props.children) will look like
$$typeof:
Symbol(react.element)
_owner:ReactCompositeComponentWrapper
_self:null
_shadowChildren:Object
_source:null
_store:Object
key:null
props:Object
ref:null
type: Tab(props, context)
__proto__
Object
Children[0].props looks like
+Children (one element)
className="home"
title="first title"
Finally Children object looks like (this is what i want to pass):
$$typeof:Symbol(react.element)
_owner:ReactCompositeComponentWrapper
_self:null
_shadowChildren:undefined
_source:null
_store:
key:null
props:Object
__proto__:Object
**type: function Statement()**
ref:null
The question is this, if I rewrite MultiTab like this
<Tabs initialIndex={1} justify="start" className="tablisty">
<Tab title="First Title" className="home" pass={Statement} />
<Tab title="Second Title" className="check" pass={SchoolDetails} />
<Tab title="Third Title" className="staff" pass={AuthorizedStaff} />
</Tabs>;
Inside the Tabs component
this.props.children looks the same as above.
children[0].props looks like
classname:"home"
**pass: function Statement()**
title: "First title"
I want the pass property to look like. Above just prints out the Statement function.
$$typeof:Symbol(react.element)
_owner:ReactCompositeComponentWrapper
_self:null
_shadowChildren:undefined
_source:null
_store:
key:null
props:Object
__proto__:Object
**type: function Statement()**
ref:null
This is a weird question, but long story I'm using a library and this is what it comes down to.
Using this.props.children is the idiomatic way to pass instantiated components to a react component
const Label = props => <span>{props.children}</span>
const Tab = props => <div>{props.children}</div>
const Page = () => <Tab><Label>Foo</Label></Tab>
When you pass a component as a parameter directly, you pass it uninstantiated and instantiate it by retrieving it from the props. This is an idiomatic way of passing down component classes which will then be instantiated by the components down the tree (e.g. if a component uses custom styles on a tag, but it wants to let the consumer choose whether that tag is a div or span):
const Label = props => <span>{props.children}</span>
const Button = props => {
const Inner = props.inner; // Note: variable name _must_ start with a capital letter
return <button><Inner>Foo</Inner></button>
}
const Page = () => <Button inner={Label}/>
If what you want to do is to pass a children-like parameter as a prop, you can do that:
const Label = props => <span>{props.content}</span>
const Tab = props => <div>{props.content}</div>
const Page = () => <Tab content={<Label content='Foo' />} />
After all, properties in React are just regular JavaScript object properties and can hold any value - be it a string, function or a complex object.
As noted in the accepted answer - you can use the special { props.children } property. However - you can just pass a component as a prop as the title requests. I think this is cleaner sometimes as you might want to pass several components and have them render in different places. Here's the react docs with an example of how to do it:
https://reactjs.org/docs/composition-vs-inheritance.html
Make sure you are actually passing a component and not an object (this tripped me up initially).
The code is simply this:
const Parent = () => {
return (
<Child componentToPassDown={<SomeComp />} />
)
}
const Child = ({ componentToPassDown }) => {
return (
<>
{componentToPassDown}
</>
)
}
By using render prop you can pass a function as a component and also share props from parent itself:
<Parent
childComponent={(data) => <Child data={data} />}
/>
const Parent = (props) => {
const [state, setState] = useState("Parent to child")
return <div>{props.childComponent(state)}</div>
}
I had to render components conditionally, so the following helped me:
const Parent = () => {
return (
<Child componentToPassDown={<SomeComp />} />
)
}
const Child = ({ componentToPassDown }) => {
return (
<>
{conditionToCheck ? componentToPassDown : <div>Some other code</div>}
</>
)
}
In my case, I stacked some components (type_of_FunctionComponent) into an object like :
[
{...,one : ComponentOne},
{...,two : ComponentTwo}
]
then I passed it into an Animated Slider, and in the the slide Component, I did:
const PassedComponent:FunctionComponent<any> = Passed;
then use it:
<PassedComponent {...custom_props} />
How about using "React.createElement(component, props)" from React package
const showModalScrollable = (component, props) => {
Navigation.showModal({
component: {
name: COMPONENT_NAMES.modalScrollable,
passProps: {
renderContent: (componentId) => {
const allProps = { componentId, ...props };
return React.createElement(component, allProps);
},
},
},
});
};

'Maximum update depth exceeded' error when trying to use custom function passed as props in 'onClick' of button

I am trying to use a pass a function as props declared in App.js to handle a state variable in App.js in order to add items to a cart component but get this error as soon as I add the function to the onClick field of the "Add" button in my product component(at the end of post):
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
My App.js looks like this:
const App = () => {
const [cartItems, setCartItems] = useState([]);
const handleAddProduct = (product) => {
//some logic to add product to cartItems list here
}
return(
<Box className="App">
<AppRoutes handleAddProduct={handleAddProduct} cartItems={cartItems}/>
</Box>
);
}
Im passing the function and the state variable as props to my routes component so I can access it in my Products page:
const AppRoutes = ({ handleAddProduct, cartItems }) => {
return (
<Routes>
<Route exact path="/alldrip" element={<ProductsPage handleAddProduct={handleAddProduct} />} />
</Routes>
)}
And in my products page the function gets passed as props again to another component:
const ProductsPage = ({ handleAddProduct }) => {
return (
<AllProducts handleAddProduct={handleAddProduct} />
)}
And then I pass the function one last time in AllProducts to an individual Product component: ( I seperated the components this way so it is easier for me to read)
const AllProducts = ({ handleAddProduct }) => {
return (
{products.map(product => {
return (
<Product handleAddProduct={handleAddProduct} product={product} />
)
})}
)}
The products load fine but the app crashes with the error as soon as I add the function to the "Onclick" of the add to cart button:
const Product = ({ handleAddProduct, product }) => {
return (
<Heading>{product.name}</Heading>
<Text >{product.material}</Text>
<Text>{product.price} </Text>
<Button onClick={handleAddProduct(product)} >Add to Cart</Button>
)}
If I remove the function the app stays alive !
I'm not understanding why the error states setState is getting called repeatedly.
onClick={handleAddProduct(product)}
This should probably only be
onClick={() => handleAddProduct(product)}
otherwise you're calling the handleAddProduct method on render directly and not on click.
You call your handleAddProduct directly in your jsx that re-renders the component that directly call handleAddProduct and so on ...
You can try
onClick={() => handleAddProduct(product)}
A better approach is to avoid anonymous functions so in your Product component
const Product = ({ handleAddProduct, product }) => {
const onAddProduct = (product) => {
handleAddProduct(product)
}
return (
...
<Button onClick={onAddProduct}>Add to Cart</Button>
)
)}

How to render based on a condition in Typescript?

Say we have a component, with the following definition:
export const MentForm: React.FC<IProps> = (props: IProps) => {
It's return is a div with a load of HTML, some of which is the following:
<div className="form">
<MentFormButton {...fProps} />
<FlyComp
user={props.user}
onClick={props.onShowFly(props.user)}
/>
{showFlyForm (
<FlyForm
onFlyed={() => {
if (props.onFlyClicked) {
props.onFlyClicked(flies)
}
setShowFlyForm(false)
}}
onFlyFail={() => {
setShowFlyForm(false)
}}
user={flies}
/>
)}
</div>
Now, when we click the button, onShowFly gets triggered
We have another TSX file that, within a Test functional component, there is a return with a <div> and that has the following:
<MentForm
user={new User}
onCancelClick={() => {
if (props.user) {
wait()
}
}}
onShowFly={() => {// HERE //}}
/>
Where I've wrote "HERE", I want to be able to have the stuff in curly braces in the MentForm component to activate.... but I don't know how to do this....
Within the component that has your showFlyForm, you can use useEffect to fire off a function whenever the state changes and then just check for showFlyForm === true or whichever logic you use to check for showing the form.
Ex:
useEffect(() => {
showFlyForm && [your HERE function]
}, [showFlyForm])

Trying to change state in Parent from Child component React Hooks

I've been looking through related posts and tried a few solutions but I wasn't able to adapt any of them to my situation.
I am trying to change the state of selectedPin by sending the function and calling it from the child component.
See below my code
Parent component :
export default function Pins(data) {
const chargers = data.data;
const [selectedPin, setSelectedPin] = useState(null);
const handleClose = () => {
setSelectedPin(null);
};
return (
<>
{chargers.map((charger, index) => (
<Marker
key={index}
longitude={charger.AddressInfo.Longitude}
latitude={charger.AddressInfo.Latitude}
>
<Button
onClick={(e) => {
e.preventDefault();
setSelectedPin(charger);
}}
>
<img src="./assets/pin.svg" alt="marker pin" />
</Button>
</Marker>
))}
{selectedPin ? (
<PopupInfo selectedPin={selectedPin} handleClose={handleClose} />
) : null}
</>
);
}
Child component :
export default function PopupInfo(selectedPin, { handleClose }) {
const pin = selectedPin.selectedPin;
console.log(pin);
console.log(handleClose);
return (
<Popup
latitude={pin.AddressInfo.Latitude}
longitude={pin.AddressInfo.Longitude}
onClose={handleClose}
>
<div>Popup Info</div>
</Popup>
);
}
When console logging handleClose in child component I am getting undefined.
I think you should pass them both down as named props:
export default function PopupInfo({ selectedPin, handleClose })
or do this:
export default function PopupInfo(props) and then use props.selectedPin and props.handleClose.
It won't work as React is Uni-directional data flow model - it'a core feature of React.
State of Parent can't be modified by Child. You have to come-up with an idea to handle the data in Parent itself or pass that as prop to bypass(not recommended)
If you are interested to know more : https://www.youtube.com/watch?v=hO8u07-WTOk

How to get the component's return value

I have some code like this:
import React from "react";
import "./styles.css";
const t = () => ["test1", "test2"];
const Translate = () => t("myText");
const MyComponent = ({ children }) => {
console.log(children);
return (
children &&
typeof children === "object" &&
Array.isArray(children) &&
children.map(val => (
<>
{`${val}__attached`}
<br />
</>
))
);
};
export default function App() {
return (
<div className="App">
<MyComponent>
<Translate />
</MyComponent>
</div>
);
}
When I check the console, I get a React element printed. But I want its return value.
For example, if I replace this code:
<MyComponent>
<Translate />
</MyComponent>
with
<MyComponent>
{t()}
</MyComponent>
Then I get an array with 2 objects printed on console. I want to see this same output in my first case.
I want it, because I want to loop through the values returned by Translate and generate output based on that.
Thank you.
Here is the codesandbox link:
You cannot achieve what you want. Indeed, when you write {t()} you actually provide an array as a children for MyComponent and can therefore map its values.
However, as soon as you write <Translate/>, children of MyComponent is no longer an array but a React Element (HTML element so to speak) (print children in MyComponent to check) making your test always false (so it prints nothing)
Try to call <Translate/> outside of MyComponent and you will see "test1test2" displayed on your page. It is no longer an array.
Instead of writing this:
<MyComponent>
<Translate />
</MyComponent>
I think you should do the other way around to be able to easily manipulate what comes from Translate and passing it to MyComponent:
<Translate>
{values => <MyComponent values={values}>}
</Translate>
with Translate being:
const Translate = ({ children }) => children(t("myText"));
and MyComponent:
const MyComponent = ({ values }) => {
return (
Array.isArray(values) &&
values.map(val => (
<>
{`${val}__attached`}
<br />
</>
))
);
};
Live demo here

Resources