Component instance as default prop - reactjs

I want to allow a component to pass in a button component or default to an instance created in the component.
Say I have a component like this:
const Form = ({ submitButton, children }) =>
<form>
{childrem}
{submitButton || <OtherComponent/>}
</form>
How can I achieve this?

You can pass button component to the props and simply use it in jsx:
const Form = ({ SubmitButtonComponent, children }) =>
<form>
{childrem}
{SubmitButtonComponent ? <SubmitButtonComponent /> : <OtherComponent />}
</form>

I think this would be a good use case for ES2015 default function parameters:
const Form = ({ SubmitButtonComponent = OtherComponent, children }) =>
<form>
{children}
<SubmitButtonComponent/>
</form>
This should be semantically equivalent to the answer given above.

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);
},
},
},
});
};

how do I change a input children value component?

I’m wondering how can I change a children input value from main?
Basically I have a component that I’m sending a children, and in this component I want to be able to change the input value.
Here is my main:
<FooterOptions>
<input type="text"
onChange={event=>setState(event.target.value)}
value={state}
hidden/>
</FooterOptions>
And here is my component:
export function FooterOptions(props:{children: ReactNode}){
return(
<div>
{props.children}
</div>
)
}
The children prop is something that you can only render onto the page, so there's nothing you can do with it to change the value of the input.
Instead how I would think about this is that you want to provide a mechanism for FooterOptions to change the value of another component. Here it happens to also be rendered as its children, but it would work the same even if it was rendered someplace else.
const Parent = () => {
const updateInput = (val) => setState(val)
return (
<FooterOptions handleUpdate={updateInput}>
<input type="text"
onChange={event=>setState(event.target.value)}
value={state}
hidden/>
</FooterOptions>
)
}
export function FooterOptions(props:{children: ReactNode, handleUpdate}){
// Example handler
const handleClick = () => handleUpdate('updated inside FooterOptions')
return(
<div onClick={handleClick}>
{props.children}
</div>
)
}
If you'd like to add more details of how you are hoping to update, then I can add a better example 😀

Defining Components in React

I am creating a react website in which many pages have a specific button that should look the same for all. Should this button be its own component? If so, how would I specify the onClick events to be different for each button if it is a component?
Yes, it should be its own component.
Create it in a separate file so you can import them where you need to use.
The component should receive a onClick prop that you pass to the internal button tag.
See this example: https://reactjs.org/docs/components-and-props.html#composing-components
export const Button = ({ label, onClick, disabled }) => {
return (
<button
onClick={onClick}
disabled={disabled}
>
{label}
</button>
)
}
and then you can export this component inside any of the components you want, and pass in values like onClick and label to make it more dynamic
export const DemoFunction () => {
const onClickHandler = () => {}
return (
<Button label="Click" onClick={onClickHandler} />
)
}

Child Component Making Use of Parents' Prop in ReactJS

I am creating a form Component that will have a Form component and an Input component. Something like this:
<Form>
<Input name="name" />
<Input name="email" />
</Form>
In my setup, labels are automatically get generated from the name prop. What I would like to do, though, is provide an option to not show labels. Now, I could do it on each <Input> component like this:
<Form>
<Input noLabel name="name" />
<Input noLabel name="email" />
</Form>
But what I would really like to do is add it to the <Form> component and have it automatically applied to each <Input> component. Something like this:
<Form noLabel>
<Input name="name" />
<Input name="email" />
</Form>
The way I envision it, when defining my <Input> component I could do a check if the noLabel prop is set on the <Form> component. Something like this:
export const Input = props => {
...
{!props.noLabel && <label>...}
<input.../>
...
}
But I can't figure out how to get access to the noLabel prop from the <Form> component so that I can check to see whether or not it is set.
Any ideas on how to do this?
I would choose the context approach, to overcome the problems I mentioned in my comment to Mohamed's solution, this would enable indirect nesting as well:
const FormContext = React.createContext();
const Form = ...;
Form.Context = FormContext; // or simply Form.Context = React.createContext();
export default ({noLabel, ...props}) => <FormContext.Provider value={{noLabel}}/>;
and then your input component will consume it either like this:
const Input = props => {
const {noLabel} = useContext(Form.Context);
return (... < your no label logic here > ...);
}
or like this:
const Input = ....;
export default props => () => {
const {noLabel} = useContext(Form.Context);
return <Input noLabel={noLabel}/>
}
In your Form component, you can use React.Children and React.cloneElement to pass the noLabel prop to the input components, something like this :
const children = React.Children.map(this.props.children, child =>
React.cloneElement(child, { noLabel: this.props.noLabel })
);
return (<form>{children}</form>);
One way to do it is manipulating form's children. Mapping over each one and inject noLabel prop. You still will need to check inside Input for a noLabel prop but it's definitely less work
const Form = ({children, noLabel}) =>{
return React.children.forEach(_, child =>{
return React.cloneElement(child, { noLabel })
})
}

passing mobx-react-form object as a prop

I'm trying to pass mobx-react-form object as a prop to a function component I created. the problem is when I call my component like that
<NameEditor form={ newFolderForm }/>
I do get the form argument inside NameEditor component but it doesn't let me 'edit' the field .. its like the field isn't editable,
but when I call the component like that
{ NameEditor({ form: newFolderForm }) }
it works perfectly fine, what am I missing ? shouldn't the both ways be the same thing in function components ?
edit: here is how I fetch the form
const NameEditor = ({ form }) => (
<form onSubmit={ form.onSubmit }>
<input { ...form.$('name').bind() }/>
<p>{ form.$('name').error }</p>
<button>Save</button>
</form>
)
thanks
make sure you are using observer() on you're function component, from the code you showed there I think you missed this part.
const NameEditor = observer(({ form }) => (
<form onSubmit={ form.onSubmit }>
<input { ...form.$('name').bind() }/>
<p>{ form.$('name').error }</p>
<button>Save</button>
</form>
))
https://mobx.js.org/refguide/observer-component.html
read how it works with Stateless function components

Resources