I want to automate adding a <Layout /> container component to all my <View /> components.
In this scenario, the Layout component doesn't know which View it is going to get, so I can't apply Relay.createContainer to the Layout since I don't have the relay container spec at declaration.
Because the View is wrapped by the Layout, the RelayRootContainer.component receives the Layout component, which is a non-relay container, and of course, it throws an error.
This is my Layout component:
export default class MainLayout extends React.Component {
render() {
return (
<div>
<Header />
{this.props.children}
<Footer />
</div>
);
}
}
After adding the Relay capabilities to the View, it now looks like this.
class HomeView extends React.Component {
render() {
<ul>
{this.props.store.posts.map(post =>
<li key={post.id}>
{post.name}
</li>
}
</ul>
}
}
const RelayHomeView = Relay.createContainer(HomeView, {
fragments: {
store: () => Relay.QL`
fragment on Store {
posts {
id,
name,
}
}
`
}
});
export default RelayHomeView;
I came up with two possible work-arounds:
Use a high order component Layout and declare the Relay Container Specs as a static property in the View. However, this breaks the convention of exporting an already built relay container
Wrap the render View in the Layout component. Unfortunately this adds a lot of boilerplate by having to import and wrap all views in a component
Related
I'm new to React and building out a design a ran into a problem.
I have a component called SideBar. I am using this component two times, one on each side of the page.
The problem is that I would like to add different components to each instance of the SideBar component. These would be lists of various items and etc. I assumed I could next component tags but the sidebar component doesn't output.
import React, { Component } from "react";
import SideBar from "./WorkspaceComponents/SideBar";
import ScrollerBox from "./WorkspaceComponents/SideBarComponents/ScrollerBox";
class Workspace extends Component {
render() {
return (
<main className="reely-workspace">
<SideBar position="SideBarLeft">
<ScrollerBox />
</SideBar>
<SideBar position="SideBarRight" />
</main>
);
}
}
export default Workspace;
Your sidebar component should receive a children prop and render it out.
Something like this:
class Sidebar extends Component {
render() {
const {children} = this.props;
return (
<div className="sidebar">
<h1>Sidebar</h1>
{children}
</div>
)
}
}
Check out this post on react docs to understand how to compose react components: https://reactjs.org/docs/composition-vs-inheritance.html
You can make your SideBar Component a wrapper component which wraps around the content given in it.
Making SideBar Component a Wrapper Component :
class Sidebar extends Component {
render() {
return (
<div className="sidebar">
// You can add any custom element here //
{this.props.children}
</div>
)
}
}
All your element passed inside the SideBar Component will now be rendered as a part of SideBar along with what it contains.
Way to consume the wrapper component:
<SideBar>
<Content1></Content1>
<Content2></Content2>
<Content3></Content3>
</SideBar>
I'm using ReactJS since just a week or two and I'm now trying to build an App using it.
I think I understood how I should make a Child Component communicates with its Parent Component passing a function as a prop.
But now I'd like to do something different and make 2 sibling Components communicate with each other.
I know I could achieve this using their common Parent Component, but I'd really love to declare some methods on one of those sibling Components and reuse them all over the App.
So here is my idea and my question: can I safely set the state of a Parent Component putting there the "this" from Child Component and then use this variable on other Components?
I already wrote this code and it's working, but I don't understand if this is a good approach or a bad one.
Here some parts of my code to let you see what I'm doing.
Parent Component:
class App extends Component{
state = {}
render(){
return <Router>
<div id="page">
<Header app={this} />
<div id="main" class="row">
<Sidebar app={this} />
<Content app={this} />
</div>
<Footer app={this} />
</div>
</Router>
}
}
Sidebar:
class Sidebar extends Component{
state = {menu: []}
componentDidMount() {
this.props.app.setState({sidebar: this})
}
populateSidebar = (sidebar) => {
this.setState({menu: sidebar})
}
render(){
if (this.state.menu.length == 0){
return null;
}
return (
<sidebar class="col-3">
<ul>
{this.state.menu.map(item => <li><Link to={item.url}>{item.text}</Link></li>)}
</ul>
</sidebar>
)
}
}
User Component (it's a Child of the Content Component. The Content Component just does some routing based on the url):
class User extends React.Component {
async componentDidMount() {
await this.props.app.state.sidebar
this.props.app.state.sidebar.populateSidebar(
[
{
url: "/user/add",
text: "Add new user"
},
{
url: "/user/list",
text: "Users list"
}
]
)
}
async componentWillUnmount() {
await this.props.app.state.sidebar
this.props.app.state.sidebar.populateSidebar([])
}
render() {
return (
<div>
<UserAdd />
<UserList />
</div>
);
}
}
I know that what I'm accomplishing here is so basic that I could totally do it in a different way, for example putting the sidebar menu as an array on the Parent Component's state. But let's say that I want a bunch of methods on Sidebar and let all my other components using them without rewriting too much code. Is this a bad idea?
I'd really love to declare some methods on one of those sibling
Components and reuse them all over the App.
A better approach is to create a helper class with some static methods and use it everywhere across your components, this class even doesn't have to be a react component just a regular ES6 class, for example:
class MyHelper {
static doSummation(num1, num2) {
return num1 + num2;
}
static doMultiplication(num1, num2) {
return num1 * num2
}
// ... other helper methods as you want
}
export default MyHelper;
Then in your React components you can import it and use its helper methods:
import React, {Component} from 'react';
import MyHelper from './MyHelper';
class MyComponent extends Component {
render() {
return (
<div>
{MyHelper.doSummation(1, 2)};
</div>
);
}
}
You can even, for better organization, have as many helper classes as you want, for example MathHelper, StringFormattingHelper, etc...
I have a link in a nav-bar that takes me to an anchor on the index page. Currently I don't know how to put an id onto the component, so I have to wrap the component in a div and give it an id for it to work. Ideally, I would like to simply put the anchor on the component itself.
This works fine for me, but I'm wondering if this is the way to do an anchor with React/Gatsby or is there a better way?
//Navbar, which is part of Layout
export default class NavBar extends Component {
render() {
return (
<NavContainer>
<Menu>
<ul>
<li>Home</li>
<li>About</li>
<li>Events</li>
<li>Blog</li>
<li>Mentorship</li>
<li>
<Link to="/#join-us">Join Us</Link>
</li>
</ul>
</Menu>
</NavContainer>
)
}
}
//Homepage
const IndexPage = ({ data, location }) => {
const { site, events, about, features, blogs } = data
const eventsEdges = events.edges
return (
<Layout>
<div id="join-us">
<JoinUs /> //Can't do <JoinUs id="join-us"/>
</div>
<BlogList blogs={blogs} fromIndex={true} />
</Layout>
)
}
You have to pass id as a props to your JoinUs component.
First of all, do <JoinUs id="join-us" />. Now, id is a props of your component.
JoinUs component
const JoinUs = ({ id }) => (
<div id={id}>
...Your component stuff
</div>
);
Other method
import React from 'react'
class JoinUs extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div id={this.props.id}>
... Your component stuff
</div>
);
}
}
export default JoinUs
The two methods are similar but the first one is more concise.
The line JoinUs = ({ id }) ... allows you to access and destructure props. You get property id from your props. Now, you don't have to wrap your component in a div with an anchor
More information here : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
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 ...
}
[...]
}