import SomeComponent from 'Somewheere';
class MyPage {
render() {
return '<OtherComponent AcceptsSomeClass={SomeComponent} />';
}
}
in OtherComponent I want to be able to do
class OtherComponent {
render() {
return <this.props.AcceptsSomeClass open={true} someOtherProp={123}/>;
}
}
I want to be able to render SomeComponent inside OtherComponent. I know I can just pass a node or a function. But I've seen a library before that accepts a class like this and I want to pass the class so that I can control it more in OtherComponent instead of deciding how it renders in MyPage and passing it thee node/function.
In other words I want to pass a class (react component) as a prop and then be able to use it in the JSX.
What I did is that we are passing a function that renders a component, then we can call that function inside the OtherComponent to render it there.
class MyPage extends React.Component {
render() {
return <OtherComponent AcceptsSomeClass={() => <SomeComponent />} />
}
}
class OtherComponent extends React.Component {
render() {
return (
<div>
<p>Content inside OtherComponent</p>
{this.props.AcceptsSomeClass()}
</div>
)
}
}
class SomeComponent extends React.Component {
render() {
return <h1>HELLO WORLD!</h1>
}
}
ReactDOM.render(<MyPage />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>
An example of which you can pass components as props is when you are dealing with HOC (higher-order-components)
I use HOC to handle HTTP requests, for instance, using a modal to pop up on the screen with the loading / error when fetching /putting data, or authentication.
I will present you with a simple example:
import React from 'react'
import Modal from 'modal' //this would be a modal covering the screen
const httpHandler = WrappedComponent => {
const wrappedComponent = props => {
//handle some logic here, coded here, or as a result from some middleware
return (
<Fragment>
<Modal>...</Modal> //handle the HTTP async stuff here, like loading,
//or authentication, or an error message
<WrappedComponent {...props} />
</Fragment>
)
}}
You can call this inside a Component like this when you export another component
//all of the component stuff above
export default httpHandler(WrappedComponent)
Related
In my code I have to use the same lines of code at different places. So i thought that's the right time to put this code into a kind of base class. I've read about Higher-Order Components which seems to be the way to go and following some examples i ended up with the following code, which is not working. I've tried something around but was not able to get it work.
My HOC:
export interface HocProps {
DynamicId: string
}
const withDiv = (hocProps) => (BaseComponent) => {
return class extend React.Component {
render() {
return (
<div id={ hocProps.DynamicId }>
<BaseComponent />
</div>
);
}
}
}
export default withDiv;
A component to be wrapped by the div:
import withDiv from './MyHoc';
class MyComponent extends React.Component {
render() {
return (
<h3>Some content here</h3>
);
}
}
export default withDiv({ DynamicId: <dynamic value> })(MyComponent);
Another component, that uses MyComponent:
import MyComponent from './MyComponent';
export class OtherComponent extends React.Component {
render() {
return (
<div>
... Some content here ...
<MyComponent DynamicId={ 'id123' } />
</div>
);
}
}
I'd like to pass an id to in OtherComponent. Then in MyComponent this id has to be passed to the HOC as , which is not working. I only can pass static values to the HOC.
I'm new to react and I think i've made same mistake(s).
So my question is: What am i doing wrong and how is it done right?
Maybe there is another/better way for this?
Thanks in advance.
Edit:
I would expect this result:
<div>
... Some content here ...
<div id='id123'>
<h3>Some content here</h3>
</div>
</div>
There are two ways you can use HOCs according to your implementation.
I have created a sandbox, which will help you understand how to use HOCs.
One way is to extract your props out const hocWrapper = Component => props => { // return NewComponent and call it too }. Here you have to call your component while returning.
Other way is to destructure or use the props inside hocWrappers. const hocWrapper = Component => { // return NewComponent, you will receive props inside the newComponent and do what you wish}
Try this
const withDiv = (BaseComponent) => {
class CompWithDiv extends React.Component {
render() {
return (
<div id={this.props.DynamicId}>
<BaseComponent />
</div>
);
}
}
return CompWithDiv ;
}
export default withDiv;
Im not sure how you pass the dynamic values and whats wrong with it. but, you can just create your component like
export interface MyCustomProps {
customProp: string;
}
export interface MyCustomState {
something: string;
}
class MyComponent extends React.Component<MyCustomProps, MyCustomState>{
render(){<div>
... Some content here ...
<div>{this.props.customProp}</div>
</div>}
}
export default MyComponent
and use it in another component like
import MyComponent from './MyComponent';
export class OtherComponent extends React.Component {
render() {
return (
<div>
... Some content here ...
<MyComponent customProp={someDynamicStringValues}/>
</div>
);
}
}
and you can do it recursively
How to add RenderFunction of class of SayHello into RenderView function?
For clarity, please look at the following picture and code.
this is my code:
import React from 'react';
class SayHello{
RenderFunction() {
return (
<p>Hello</p>
);
}
}
function RenderView(){
return (
<div>
//I want to add RenderFunction of class of SayHello into here.
</div>
);
}
const DishDetail = () => {
return(
<div>
<RenderView />
</div
);
}
export default DishDetail;
I believe the following is what you are looking for, if I am understanding the problem you are asking. The following can be seen working here: https://codesandbox.io/s/7ykk0z90yx
Firstly we are going to be extending our react component, we can do this in two way, we can destructure from the react import like the following.
import React, { Component } from 'react';
Or we can just directly write it:
class SayHello extends React.Component
Secondly we need to call our SayHello component like we would with any other component we want to use.
Also as we are using arrow functions, I went with this approach for the RenderView component.
And with the above we have the following:
class SayHello extends Component {
render() {
return <p>Hello</p>;
}
}
const RenderView = () => {
return (
<div>
<SayHello />
</div>
);
};
const DishDetail = () => {
return (
<div>
<RenderView />
</div>
);
};
Hope the above helps and answers your question. I have also tidied up your code. For example your original code is missing an ending > on it's div. Which obviously will not make it compile/help.
did you try like this?
class SayHello{
RenderFunction = () => {
return (
<p>Hello</P>
);
}
}
function RenderView(){
return SayHello.RenderFunction();
}
If RenderFunction doesn't depend on SayHello instance, it shouldn't be a part of this class.
It appears like stateless function component and can be used as such because this is how composition is commonly implemented in React:
function RenderFunction() {
return (
<p>Hello</P>
);
}
class SayHello{
// can use <RenderFunction> too if necessary
}
function RenderView(){
return <RenderFunction/>
}
In case RenderFunction depends on SayHello instance (this.state), this is a different problem because accessing RenderFunction isn't enough. On the contrary, RenderFunction shouldn't be additionally called because this will create additional Modal instances. It's existing SayHello component instance that needs to be accessed in other components.
SayHello should be a parent or a sibling to components that need to interact with a modal.
There should exist a reference to SayHello instance:
<SayHello ref={this.modalContainerRef}/>
Then a modal can be reached as this.modalContainerRef.current.toggleModal() in parent component. A callback that allows to call toggleModal can be passed to child components through props or context API.
extend your class from Component.
export class SayHello extends Component {...}
move your RenderFunction into render as arrow function.
put <SayHello /> into methodof RenderView.
View the correct code below:
import React, { Component } from 'react';
export class SayHello extends Component {
render(){
RenderFunction = ()=> {
return (
<p>Hello</p>
);
}
return(
{RenderFunction()}
);
}
}
function RenderView(){
return (
<div>
<SayHello />
</div>
);
}
const DishDetail = ()=>{
return(
<div>
<RenderView />
</div
);
}
export default DishDetail;
I may be over thinking this, but I am curious if importing a child component directly is bad practice with regards to coupling and testing.
Below is a simple example:
import Header from './header.jsx';
class Widget extends React.Component {
render() {
return (
<div>
<Header></Header>
<div>{this.props.importantContent}</div>
</div>
)
}
}
To me it looks like there is now coupling between Widget and Header. With regards to testing, I don't see an easy way to mock the Header component when testing the Widget component.
How do other larger React apps handle cases like this? Should I pass Header in as a prop? If using react-redux, I can inject header with the Connect method like below to reduce boilerplate. Is that sound?
import { connect } from 'react-redux';
import Header from './header.jsx';
class Widget extends React.Component {
render() {
return (
<div>
{this.props.header}
<div>{this.props.importantContent}</div>
</div>
)
}
}
const mapStateToProps = state => {
return {
header: Header
}
}
export default connect(mapStateToProps)(Widget)
I am interested is simple doing what the community is generally doing. I see that one solution is doing shallow rendering to test on the main part of the component and not the child components using something like Enzyme.
Thoughts or other ideas?
Passing elements / components as props is a good idea. Having default props is a good idea too:
const Widget = ({
header = <div>Default Header.. </div>,
content = <div>Default Content.. </div>
}) =>
<div>
{header}
{content}
</div>
Then elsewhere in your app:
<Widget header={<Header title="Foo" />} content="content from props" />
No need to inject using connect
You can also pass a component, not just an element if you want to interact with props / send data back to parent:
const Widget = ({
Header = props => <div>Default Header.. </div>,
Content = props => <div>Default Content.. </div>
}) =>
<div>
<Header />
<Content />
</div>
Elsewhere:
<Widget Header={Header} Content={props => <Content />} />
As long as the component always renders the same thing it can be directly rendered as a child rather than the parent.
If all other portions of the Component remain constant and only the Header can be different across pages then you could actually implement it as an HOC instead of passing it as a props
const MyCompFactory = ({CustomHeader = DefaultHeader}) => {
return class Widget extends React.Component {
render() {
return (
<div>
<CustomHeader/>
<div>{this.props.importantContent}</div>
</div>
)
}
}
}
and use it like
const CustomComponent = MyCompFactory({CustomComponent: Header})
as long as testing is concerned in your case, you could just shallow render your component and then Search if the Header component is rendered something like
import Header from 'path/to/header'
const component = shallow(
<Widget {...customProps}/>
)
test('test' , () => {
expect(component.find(Header).exists()).toBe(true)
})
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 ...
}
[...]
}
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.