Creating a parent 'workspace' component in ReactJS - 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>
)
}

Related

Using the React Children code example is not working

"Using the React Children API" code example is not working, tried several syntax options, seems the problem is not quite clear.
http://developingthoughts.co.uk/using-the-react-children-api/
class TabContainer extends React.Component {
constructor(props) {
super();
this.state = {
currentTabName: props.defaultTab
}
}
setActiveChild = (currentTabName) => {
this.setState({ currentTabName });
}
renderTabMenu = (children) => {
return React.Children.map(children, child => (
<TabMenuItem
title={child.props.title}
onClick={() => this.setActiveChild(child.props.name)}
/>
);
}
render() {
const { children } = this.props;
const { currentTabName } = this.state;
const currentTab = React.Children.toArray(children).filter(child => child.props.name === currentTabName);
return (
<div>
{this.renderTabMenu(children)}
<div>
{currentTab}
</div>
</div>
);
}
}
When I changed code like this, it compiles finally
import React from "react";
import ReactDOM from "react-dom";
import "./index.css";
const TabMenuItem = ({ title, onClick }) => (
<div onClick={onClick}>
{title}
</div>
);
class TabContainer extends React.Component {
constructor(props) {
super();
this.state = {
currentTabName: props.defaultTab
}
}
setActiveChild = ( currentTabName ) => {
this.setState({ currentTabName });
}
renderTabMenu = ( children ) => {
return React.Children.map(children, child => (
<TabMenuItem
title={child.props.title}
onClick={() => this.setActiveChild(child.props.name)}
/>
))
}
render() {
const { children } = this.props;
const { currentTabName } = this.state;
const currentTab = React.Children.toArray(children).filter(child =>
child.props.name === currentTabName);
return (
<div>
{this.renderTabMenu(children)}
<div>
{currentTab}
</div>
</div>
);
}
}
ReactDOM.render(<TabContainer />, document.getElementById("root"));
Not quite experienced with JS and React, so my questions:
1) should this.setActiveChild be used as this.props.setActiveChild?
2) renderTabMenu = ( children ) or renderTabMenu = ({ children })
3) how to fill this page with some content? I don't see any physical children actually present =)
4) don't get the point why bloggers put the code with errors or which is difficult to implement, very frustrating for newcomers
5) any general guidance what can be not working in this example are welcome
Using React.Children or this.props.children can be a bit of a level up in your understanding of React and how it works. It'll take a few tries in making a component work but you'll get that aha moment at some point. In a nutshell.
this.props.children is an array of <Components /> or html tags at the top level.
For example:
<MyComponent>
<h1>The title</h1> // 1st child
<header> // 2nd child
<p>paragraph</p>
</header>
<p>next parapgraph</p> // 3rd child
</MyComponent>
1) should this.setActiveChild be used as this.props.setActiveChild?
Within the TabContainer any functions specified within it need to be proceeded with this. Within a react class this refers to the class itself, in this case, TabContainer. So using this.setActiveChild(). will call the function within the class. If you don't specify this it will try to look for the function outside of the class.
renderTabMenu = ( children ) or renderTabMenu = ({ children })
renderTabMenu is a function which accepts one param children, so call it as you would call it as a normal function renderTabMenu(childeren)
How to fill this page with some content? I don't see any physical children actually present =)
Here's where the power of the TabsContainer comes in. Under the hood, things like conditional rendering happen but outside of it in another component you specify the content. Use the following structure to render home, blog, and contact us tabs.
<TabsContainer defaultTab="home">
<Tab name="home" title="Home">
Home Content
</Tab>
<Tab name="blog" title="Blog">
Blog Content
</Tab>
<Tab name="contact" title="Contact Us">
Contact content
</Tab>
</TabsContainer>
I know how hard it is to make some examples work especially when you are starting out and are still exploring different concepts that react has to offer. Luckily there's stack overflow :).
Here's real live example to play around with, visit this CodeSandBox.

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.

React - pass object from Container Component to Presentational Component

I'm trying to dynamically add Components (based on ID from an array) into my Presentational Component. I'm new to all this so there is a possibility I'm making it way too difficult for myself.
Here's the code of my Container Component:
class TemplateContentContainer extends Component {
constructor() {
super()
this.fetchModule = this.fetchModule.bind(this)
this.removeModule = this.removeModule.bind(this)
this.renderModule = this.renderModule.bind(this)
}
componentWillReceiveProps(nextProps) {
if(nextProps.addAgain !== this.props.addAgain) // prevent infinite loop
this.fetchModule(nextProps.addedModule)
}
fetchModule(id) {
this.props.dispatch(actions.receiveModule(id))
}
renderModule(moduleId) {
let AddModule = "Modules.module" + moduleId
return <AddModule/>
}
removeModule(moduleRemoved) {
console.log('remove clicked' + moduleRemoved)
this.props.dispatch(actions.removeModule(moduleRemoved))
}
render() {
return (
<div>
<TemplateContent
addedModule={this.props.addedModule}
templateModules={this.props.templateModules}
removeModule={this.removeModule}
renderModule={this.renderModule}
/>
</div>
)
}
}
and the code of the Presentational Component:
const TemplateContent = (props) => {
let templateModules = props.templateModules.map((module, index) => (
<li key={index}>
{props.renderModule(module)}
<button onClick={props.removeModule.bind(this, index)}>
remove
</button>
</li>
))
return (
<div>
<ul>
{templateModules}
</ul>
</div>
)
}
the renderModule function returns object, but when it's being passed to the presentational Component it doesn't work anymore (unless it's passed as className for example then it returns object)
I'm importing the modules from modules folder where I export them all into index.js file
import * as Modules from '../components/modules'
Hope it makes sense, any help would be highly appreciated.
Thanks a lot in advance!
I would recommend to restructure the files to make for an easier handling.
If your container components render would look like this:
render () {
return (
<div>
<ul>
{this.props.templateModules.map(module => (
<ChildComponent onRemove={this.removeModule} module={module} />
)}
</ul>
</div>
)
}
Your child component can just handle the remove click and the displaying of the module data
EDIT:
My bad, I just misunderstood your problem.
I would map the ids to the according components instead of concatenating the name of the Component you want to render, so your container component would look something like this:
getChildComponent (id) {
const foo = {
foo: () => {
return <Foo onRemove={this.removeModule} />
},
bar: () => {
return <Bar onRemove={this.removeModule} />
}
}
return foo[id]
}
render () {
return (
<div>
<ul>
{this.props.templateModules.map(module => (
{this.getChildComponent(module.id)()}
)}
</ul>
</div>
)
}
Also you should maybe have a look at react-redux and move your dispatches to react-redux containers.

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