This section will outline the React Component Container/Smart/Controller Pattern.
You will hear this pattern called container components, smart components, or controller components/views. Basically, when you wrap a component around presentational/dumb/stateless component(s) and, the outer component deals only with setting and getting data/state/store stuff, that is then pass down to the presentational/dumb/stateless components via props/context, you are dealing with a container/smart/controller component pattern.
Below are two sudo components contrasting the difference between a container/smart/controller component and presentational/dump/stateless component
CommentList.js
is a presentational/dump/stateless
component (i.e. it takes data in and displays it):
import React from "react";
const Commentlist = ({comments}) => (
<ul>
{comments.map(({ body, author }) =>
<li>{body}-{author}</li>
)}
</ul>
)
CommentListContainer.js
is a container/smart/controller
component (it manages data and sends it to other components for
display):
import React from "react";
import CommentList from "./CommentList";
class CommentListContainer extends React.Component {
state = { comments: [] }
componentDidMount() {
fetch("/my-comments.json")
.then(res => res.json())
.then(comments => this.setState({ comments }))
}
render() {
return <CommentList comments={this.state.comments} />;
}
}
The code example below is another example of the container/smart/controller component:
The container/smart/controller
<CounterContainer>
component manages the state
while the dump/stateless <Count>
and
<Button>
components consume state related
concerns from the container component above it via props.
This section will outline the React High-Order Component Pattern.
A higher order component is a javascript function that takes a React component as an argument, wraps the passed component with another component (i.e. a Container/Smart/Controller Component Pattern) and then returns this wrapped component.
Imagine you want several components to share the same exact prop. This can be done by creating a HOC to wrapped any component so it always gets exactly the same prop (Note: would work with state too, not just props).
HOC (withReactNameProp.js
):
import React from "react";
function withReactNameProp(WrappedComponent) {
// this component wraps the component passed in i.e. WrappedComponent
return class extends React.Component {
// give it name in component tree otherwise name is generic "_class"
static displayName = "withReactNameProp";
render() {
return <WrappedComponent name="React" {...this.props} />;
}
};
}
export default withReactNameProp;
Component to be wrapped (Hello.js
):
import React from "react";
import withReactNameProp from "./withReactNameProp";
// foo comes from <Hello foo="bar">
// the name "React" comes from HOC
const Hello = ({ name, foo }) => {
return (
<>
{foo} {/* prints "bar" works because of {...this.props} in HOC */}
<h1>Hello {name}!</h1> {/* prints "React" works because HOC passes name prop */}
</>
);
};
// Hello becomes <withReactNameProp><Hello /></withReactNameProp>
export default withReactNameProp(Hello);
Using the Wrapped Component (index.js
):
import React from "react";
import ReactDOM from "react-dom";
import Hello from "./Hello";
// <Hello /> is really <withReactNameProp><Hello /></withReactNameProp>
ReactDOM.render(<Hello foo="bar" />, document.getElementById("root"));
When you want to share the details of component with any number of other components. A common use case is sharing the same state setup with any number of components so each instance will have its own state setup but you only have to define the state setup once (i.e. the HOC pattern is a DRY'ing (don't repeat yourself) technique so you can reuses parts of program without duplicating the program over and over).
Below is an example of using this pattern to share state for
<Counter />
:
This section will outline the Children Props Pattern.
The props
value passed to React components has a
special value on it called props.children
. The
children
value is the child value of the component when
the component is used (e.g.
<Component> this is the children value here
</Component>
).
The react documentation explains props.children
as,
"props.children is available on every component. It contains the
content between the opening and closing tags of a component.".
If you think about it the children value of a component can be any type of JavaScript value not just strings, components, or JSX (e.g. could be a function too or really any javascript expression. Keep this in mind when you look at the Render Props Pattern).
In the
code example
below what do you think is the text shown on the
<Button>
when it is viewed in the UI (i.e.,
"delete" or "I'm available for composition"?)
import React from "react";
import ReactDOM from "react-dom";
const Button = () => {
return <button>delete</button>;
};
ReactDOM.render(
<Button>I'm available for composition</Button>,
document.getElementById("root")
);
If you said "delete" then you are correct. But what if we didn't
wanted the buttons text to be hard coded inside of the
<Button>
? What if we wanted to pass the button's
text when the component is used (i.e. making the
<Button>
reusable). To do this we could use
props
, but we could also use
props.children
which contains:, "the content between
the opening and closing tags of a component".
import React from "react";
import ReactDOM from "react-dom";
const Button = (props) => {
return <button>{props.children}</button>;
};
ReactDOM.render(
<Button>delete</Button>,
document.getElementById("root")
);
Now the
<Button>
will always show the child content
passed to the <Button>
(this facilitates
composition)
If the props.children
value was not available then
composition would all have to happen via props
alone.
Which could get really unwieldy and ugly fast if you have a lot
composition going on (i.e. a lof other components as children). So
props.children
can be used when you need to output the
contents of a component to the UI.
The example below contrast both props
and
props.children
composition:
function Posts(props){
return (
<div className="posts">
{props.children}
</div>
)
}
function Share(props){
return (
<div>
<button>{props.name}</button>
</div>
)
}
<Posts> {/* Post wraps the JSX and Share components */}
<h1>this my heading</h1>
<p>this is big old post</p>
<Share name="facebook"/> {/* uses props but could have used props.children */}
<Share name="twitter"/> {/* uses props but could have used props.children */}
</Posts>
<Button>button text</Button>
).
<Modal title="alert"> <Alert> the site is
down </Alert> </Modal>
).
This section will outline the React Render Props Pattern.
The render props pattern involves passing a function, that returns a
component/JSX, via props
to a component that then
invokes that function, providing it with arguments (typically the
arguments have things you want to reuse).
Imagine you want several components to share the same value. This
can be done by using a render prop on the Component containing the
value you want to share. For example, what if you wanted to share
the value { name: "react" }
with two or more components
and you don't want to repeat yourself.
import React from "react";
import ReactDOM from "react-dom";
// create a component that shares stuff e.g. { name: "react" }
const NameReactViaRenderProp = props => {
// could share state using class component or hooks too
return props.render({ name: "react" });
};
// Create a Hello component that share {name: 'react'}
// Really {name: 'react'} could be any value
const Hello = () => {
return (
<NameReactViaRenderProp
render={({ name }) => {// render prop
// a function passed to a component via props
return <h1>Hello {name}!</h1>;
}}
/>
);
};
ReactDOM.render(
<>
<Hello /> <Hello />
</>,
document.getElementById("root")
);
Not unlike the React HOC pattern the Render Props Pattern is used to reuses the details of another component by wrapping a container component around the a dumb/presentation/stateless component.
Note that one does see a similar pattern using only
props.children
:
import React from "react";
import ReactDOM from "react-dom";
// create a component that shares stuff e.g. { name: "react" }
const NameReactViaPropsChildren = (props) => {
// could share state using class component or hooks too
return props.children({ name: "react" });
};
// Create a Hello component that uses {name: 'react'}
// Reacly {name: 'react'} could be any value
const Hello = () => {
return (
<NameReactViaPropsChildren>
{/* a function passed to a component via props.children */}
{({ name }) => {
return <h1>Hello {name}!</h1>;
}}
</NameReactViaPropsChildren>
);
};
ReactDOM.render(
<>
<Hello /> <Hello />
</>,
document.getElementById("root")
);
When you want to re-use the details of component with any number of other components. A common use case is sharing the same state setup with any number of components so each instance will have its own state setup but you only have to define the state setup once (i.e. the Render Props pattern is a code DRY'ing (don't repeat yourself) technique so you can reuses parts of program without duplicating the program over and over).
Below is an example of using this pattern to share state for
<Counter />
: