ReactJS: making props available in static function of composed component - reactjs

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 ...
}
[...]
}

Related

How do I render a class (react component) passed as a prop?

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)

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

React: Is it bad practice to import a child component directly rather than pass in as a dependency?

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

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

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.

Pass component tag as a props in react native

It could be a silly question but I was wondering is there any way to pass a component as props to component tag.something like this
const Main = (props) => {
return (
<header>
main
</header>
<Content>
<{props.homePage} /> //this should be like this <Home />
</Content>
<Footer>
</Footer>
);
};
I have a main page that contains header, content, and footer and I want to change dynamically the component in the content.but whenever I pass the component as props react take it as a raw text and give me an error. Please, tell me if this method is the correct way or if I have to use another way.
You were very very close, it should look something like:
const Page1 = (props) => <div>my first page</div>;
const Main = (props) => <div>{props.content}</div>;
class App extends React.Component {
render () {
return (
<div>
Header
<Main content={<Page1 />} />
Footer
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'));
http://codepen.io/cjke/pen/VPYJpK?editors=0010
As an alternative, and probably a bit more natural is to leverage children:
const Page1 = (props) => <div>my first page</div>;
const Main = (props) => <div>{props.children}</div>;
class App extends React.Component {
render () {
return (
<div>
Header
<Main><Page1 /></Main>
Footer
</div>
);
}
}
ReactDOM.render(<App/>,document.getElementById('root'));

Resources