React. How to pass props inside a component defined on a prop? - reactjs

If we have the following structure on a React application:
class BasePage extends React.Component {
render() {
return <div>
{this.props.header}
{/*<Header title={this.props.title} />*/}
</div>
}
}
BasePage.defaultProps = {
header: <header>Base Page</header>
}
class Header extends React.Component {
render() {
return <header>
<h1>{this.props.title}</h1>
</header>
}
}
class TestPage extends BasePage {
}
TestPage.defaultProps = {
header: <Header />
}
class Root extends React.Component {
render() {
return <div>
<TestPage
title="Test Page Title"
/>
</div>
}
}
ReactDOM.render(
<Root />,
document.getElementById('root')
)
If we have a common component like <Header /> we can pass a title property easily like <Header title={this.props.title} />.
But how can we pass props inside a component if this component is defined as a prop itself?
For example, how can we do something like:
{this.props.header title={this.props.title}}
So it will render the Test Page Title correctly?
Important note: we could overwrite the render method inside the Test component. But the purpose of this question is to solve this problem without doing this.

Firstly, props are read-only and a component should never be update it's own props, so lines like
componentWillMount() {
this.props.header = <header>Base Page</header>
}
should not be used. defaultProps can do what I think you are trying to do:
class BasePage extends React.Component {
render() {
return <div>
{this.props.header}
{/*<Header title={this.props.title} />*/}
</div>
}
}
BasePage.defaultProps = {
header: <header>Base Page</header>
}
Secondly, inheritance is not often done in React. I'm not saying don't do what your'e doing, but take a read of the docs and see if there is perhaps a simpler way to achieve the same goals.
Finally, setting props on components passed as props. There are a couple different ways to do this.
If you pass the Component rather than the <Component /> you can add props like normal:
ChildComponent = (props) => {
const HeaderComponent = props.header
return <HeaderComponent title="..." />
}
ParentComponent = () => <ChildComponent header={Header} />
You can clone the element to override props:
ChildComponent = (props) => {
const HeaderComponent = React.cloneElement(props.header. { title: "..." })
return <HeaderComponent />
}
ParentComponent = () => <ChildComponent header={<Header />} />
NOTE: I have used functional components instead of class components for brevity, but the concepts are the same.

This seems like a great use case for React.cloneElement.
React.cloneElement(this.props.header, { title: this.props.title });
It returns a clone of the component with the new props included.

Related

Creating a parent 'workspace' component in ReactJS

Using ReactJS, I am trying to create a common workspace component that will have toolbar buttons and a navigation menu. The idea I have is to re-use this component to wrap all other dynamic components that I render in the app.
Currently, I've created a Toolbar and MenuBar components that I then add to each component in the app as such:
<Toolbar/>
<MenuBar/>
<Vendors/>
This does not feel right, since my aim is to have just one component which would be something like:
<Workspace>
<Vendor/>
</Workspace>
However, I am not sure of how to achieve this and whether this is the right approach.
As to whether or not it is the right approach is subjective, but I can provide insight into one way to make a "wrapper" type component:
// Your workspace wrapper component
class Workspace {
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
Toolbar goes here
</div>
<div className="workspace__nav">
Navgoes here
</div>
<div className="workspace__content">
{this.props.children}
</div>
</div>
)
}
}
// Using the component to define another one
class MyComponent {
render() {
return (
<Workspace>
This is my workspace content.
</Workspace>
)
}
}
You can also look at HOC's or Higher Order Components to wrap things.
React offer two traditional ways to make your component re useable
1- High-order Components
you can separate the logic in withWorkspace and then give it a component to apply that logic into it.
function withWorkSpace(WrappedComponent, selectData) {
// ...and returns another component...
return class extends React.Component {
render() {
// ... and renders the wrapped component with the fresh data!
// Notice that we pass through any additional props
return <WrappedComponent data={this.state.data} {...this.props} />;
}
};
}
const Component = () => {
const Content = withWorkSpace(<SomeOtherComponent />)
return <Content />
}
2- Render Props
or you can use function props then give the parent state as arguments, just in case you need the parent state in child component.
const Workspace = () => {
state = {}
render() {
return (
<div className="workspace">
<div className="workspace__toolbar">
{this.props.renderTollbar(this.state)}
</div>
<div className="workspace__nav">
{this.props.renderNavigation(this.state)}
</div>
<div className="workspace__content">
{this.props.children(this.state)}
</div>
</div>
)
}
}
const Toolbar = (props) => {
return <div>Toolbar</div>
}
const Navigation = (props) => {
return <div>Toolbar</div>
}
class Component = () => {
return (
<Workspace
renderNavigation={(WorkspaceState) => <Navigation WorkspaceState={WorkspaceState} />}
renderTollbar={(WorkspaceState) => <Toolbar {...WorkspaceState} />}
>
{(WorkspaceState) => <SomeComponentForContent />}
</Workspace>
)
}

In React, can I create a Component that also acts as a Forwarded Ref object?

I have a need to use forwarded refs
const InfoBox = React.forwardRef((props, ref) => (
<div ref={ref} >
<Rings >
</Rings>
<Tagline />
</div>
));
I also happen already have the code written like this
class InfoBox extends React.Component {
constructor(props) {
super(props)
}
render () {
return (
<div >
<Rings />
<Tagline />
</div>
)
}
basically my InfoBox needs to be a Component because it holds some state, but I also want it to behave like an object that can receive refs from the parent and forward them down to the children (basically React.forwardRef)
After familiarizing myself with React.forwardRef, I can't figure out how to get it to work with my existing React components, which already have functionality attached to state.
do I need to separate the two objects, and wrap one within the other or is there a way I can achieve this in the same object?
the code that wraps Infobox looks like
class AppContainer extends React.Component {
constructor(props) {
super()
this.infobox_ref = React.createRef()
}
componentDidMount() {
// this.infobox_ref.current.innerHTML should return the inner HTML of the infobox
}
render() {
return (
<InfoBox ref={this.infobox_ref}>
)
}
am I using forwarded refs correctly?
In React, the ref prop is not forwarded by default. In order to get a reference in a child component, you have 2 options:
Using a function component wrapped in the forwardRef function. You have already done this:
const InfoBox = React.forwardRef((props, ref) => (
<div ref={ref} >
<Rings >
</Rings>
<Tagline />
</div>
));
Changing the name of the ref prop.
// Parent Component
class AppContainer extends React.Component {
constructor(props) {
super()
this.infobox_ref = React.createRef()
}
componentDidMount() {
// this.infobox_ref.current.innerHTML should return the inner HTML of the infobox
}
render() {
return (
<InfoBox infoboxRef={this.infobox_ref}>
)
}
}
// Child Component
class InfoBox extends React.Component {
render () {
return (
<div ref={this.props.infoboxRef}>
<Rings />
<Tagline />
</div>
)
}
}
Of course, you can also combine them, allowing you to still pass to the ref prop from the parent, but consuming the "fixed" prop in the child class component, as shown here by #tubu13:
class InfoBox extend React.Component{
render() {
return (
<div ref={this.props.infoboxRef}>
<Rings />
<Tagline />
</div>
)
}
}
export default React.forwardRef((props, ref) => <InfoBox {...props} infoboxRef={ref} />)

React: how-to override child component

Which is the best way to override a child component?
check if this.props.children != null
passing the component with props
?
Example component
class ParentComponent extends Component {
render() {
<div>
<ChildComponent />
</div>
}
}
Solution 1
class ParentComponent extends Component {
render() {
<div>
{this.props.children ? this.props.children : <DefaultChildComponent/>}
</div>
}
}
<ParentComponent><MyChildComponent/></ParentComponent>
Solution 2
class ParentComponent extends Component {
render() {
<div>
{this.props.child}
</div>
}
}
Parent.defaultProps = { child: <DefaultChildComponent /> }
const myChildComponent = <MyChildComponent/>
<ParentComponent child={myChildComponent}/>
Solution 3?
What you are asking is slightly confusing as you are checking for this.props.children but then rendering other components rather than the children.
I would have thought the best way would be;
class ParentComponent extends Component {
render() {
<div>
{
this.props.children
? this.props.children
: <DefaultChildComponent />
}
</div>
}
}
Then use it as either;
<ParentComponent />
Which just renders your default child component
or
<ParentComponent><div>Can Be Any Child</div></ParentComponent>
Which you can pass any component is as this children which will then be rendered.

ReactJS: making props available in static function of composed component

I have a higher order component, that provides the complete website layout, including a sidebar. This sidebar contains some static elements (e.g. a logo, the home button, imprint and disclaimer) and some dynamic content, provided by the composed component.
const HtmlSkeleton = (ComposedComponent) => {
class Wrapper extends Component {
[...]
render() {
return (
<div>
<Sidebar content={ComposedComponent.getSidebarContent()} />
<Header />
<Content>
<ComposedComponent {...this.props} />
</Content>
</Footer />
</div>
)
}
}
return connect(...)(Wrapper)
}
Then I have some views/containers, that will be composed by that HOC:
class View extends Component {
static getSidebarContent = () => {
// how to access someFunction?!?
return [...some stuff depending on props. How do I have access?]
}
constructor(props) {
[....]
}
[...]
someFunction = () => { [....] }
render() {
this.someFunction()
[...]
}
}
export default connect(....)(HtmlSkeleton(View))
(connect is from react-redux, but not that important in that case)
Or is there any other possibility to keep the layout in some kind of component, that I don´t see? Where do you keep the basic HTML?
I´m using react-router, react-redux and redux-saga.
You can pass the props directly to your getSidebarContent:
<Sidebar content={ComposedComponent.getSidebarContent(this.props)} />
And retrieve them in your function:
class View extends Component {
static getSidebarContent = (props) => {
return ...
}
[...]
}

How can I pass props to children of React Router?

I have checked this link
So far, I'm not able to understand the handler part. So I was hoping for a more simple example perhaps?
Here is my main parent component:
class appTemplate extends React.Component {
render() {
return (
<div>
<Header lang={this.props.route.path}/>
{this.props.children}
<Footer lang={this.props.route.path}/>
</div>
);
}
}
What I want to do is pass down the prop this.props.route.path to my child components which is this.props.children.
I'm not really fully familiar with all the terms even though I've been touching React already for the last few months.
An example with a proper explanation would be greatly appreciated. Thanks.
The proper way to achieve that is using React.Children.map()
class appTemplate extends React.Component {
render() {
return (
<div>
<Header lang={this.props.route.path}/>
{React.Children.map(
this.props.children,
child => React.cloneElement(child,
{
customProp: "Here is your prop",
path: this.props.route.path
})
)}
<Footer lang={this.props.route.path}/>
</div>
);
}
}
React has a cloneElement function. The idea is to clone the children object, passing on path as a part of the props:
class appTemplate extends React.Component {
render() {
let children = null;
if (this.props.children) {
children = React.cloneElement(this.props.children, {
path: this.props.route.path
})
}
return (
<div>
<Header lang={this.props.route.path}/>
{children}
<Footer lang={this.props.route.path}/>
</div>
);
}
}
You should then be able to access the path using this.props.path within a child element, but (from what I remember) not from within elements nested within the child.
You can read more about cloning and passing values here:
https://facebook.github.io/react/docs/top-level-api.html#react.cloneelement

Resources