react-containers
Usage
npm install --save react-containers
import {
Center
Render
LeftRightSection,
InlineItems,
findChild,
MappingOver,
WrappingChildren
} from 'react-containers'
Motivation
The following components can be used to write a more declarative React components. These components combined with others like styled-components for example provides with a much more easier to read source code.
Take for example the source code of a component below
render() {
return (
<div className={css.container}>
{ this.state.items.map( (item,index) => {
return (
<Item
onClick={this.onItemClicked}
key={index}
data={item}>
{ item.name }
</Item>
)
})}
</div>
)
}
Although the code above works, it is much easier to read something like the one below
...
renderItem = (item, index) => (
<Item
onClick={this.onItemClicked}
key={index}
data={item}>
{ item.name }
</Item>
)
render() {
return (
<Container>
<MappingOver collection={this.state.items}>
{ this.renderItem }
</MappingOver>
</Container>
)
}
...
Components
Left and Right Sections
Provide a left and/or right section. The first element is the left section while the second element is the right section. Note that if you only require a right section then make sure that the first element is not empty.
Left And Right Section
import { LeftRightSection, Center } from 'react-containers';
//left and right
<LeftRightSection>
<ProductTitle>Cool Product</ProductTitle>
<Center><Menus/></Center>
</LeftRightSection>
Right Side Only
import { LeftRightSection, Center } from 'react-containers';
<LeftRightSection>
<div/>
<Center><Menus/></Center>
</LeftRightSection>
InlineItems
Renders item inline with a spacing. Similar to something like what flex will do by default.
render() {
return (
<div style={{display: 'flex'}}>
<div className={'spacer'}><MySpecialElement/></div>
<div className={'spacer'}><MyOtherElement/></div>
</div>
)
}
can be replaced with something like
render() {
return (
<InlineItems>
<Container><MySpecialElement/></Container>
<Container><MyOtherElement/></Container>
</InlineItems>
)
}
To prevent repetition, you can do something like
render() {
return (
<InlineItems container={Container}>
<MySpecialElement/>
<MyOtherElement/>
</InlineItems>
)
}
Center
This container will center horizontally and vertically a component rendered inside.
import { Center } from 'react-containers'
<div style={{width: 250, height: 250}}>
<Center><SomeComponent /></Center>
</div>
You can also choose to specify the dimension of the container...
import { Center } from 'react-containers'
<Center style={{width: 250, height: 250}}>
<SomeComponent />
</Center>
Render if
Will call the function child if ifTrue attribute is true. Note that the child can be an element or a function but it is advisable to pass a function if you dont want the element created in cases where ifTrue attribute is initially false.
<RenderIf expr={this.props.shouldRender}>
{ (props) => <MyComponent {...props} /> };
</RenderIf>
//below is the same but the function child is not created on subsequent render
class MyComponent extends React.Component {
showToggledView = () => {
return <ToggledView {...this.props.toggledProps }/>;
}
render() {
return (
<div>
<SomeComponent />
<Render ifTrue={this.props.isToggled}>
{ this.showToggledView }
</RenderIf>
</div>
)
}
}
MappingOver
Map over a given collection specifying the function that returns the component that maps over the element. Make sure your renderer adds a key
import { MappingOver } from 'react-containers'
...
renderItem = (item, index) => (
<Item
onClick={this.onItemClicked}
key={index}
data={item}>
{ item.name }
</Item>
)
render() {
return (
<Container>
<MappingOver collection={this.state.items}>
{ this.renderItem }
</MappingOver>
</Container>
)
}
...
The component can also use an functional component as the child. MappingOver will automatically create a React Element.
const Item = (props) => {
const { element, index, collection, data } = props
return (
<div key={index}>{index}</div>
)
}
// from another component
return (
<Container>
<MappingOver collection={this.state.items}>
{ Item }
</MappingOver>
</Container>
)
findChild
findChild is particularly useful for finding elements when you want to create tagger elements. Consider the following component
// MyComponent.js
export const Header = (props) => {
throw new Error('should not render')
}
export const Body = (props) => {
throw new Error('should not render')
}
export const Title = (props) => {
throw new Error('should not render')
}
export const App = (props) => {
const titleElement = findChild(Title, props)
const headerElement = findChild(Header, props)
const bodyElement = findChild(Body, props)
return (
<Container>
<LeftRightSection>
<TitleContainer>{ titleElement.props.children }</TitleElement>
<MainHeaderContainer>{ headerElement.props.children }</TitleElement>
</LeftRightSection>
<BodyContainer>
{ bodyElement.props.children }
</BodyContainer>
</Container>
)
}
Now your users will just create MyApp with something like
import { Header, Body, Title, App } from 'myawesomeapp'
render() {
return (
<App>
<Title><Strong>My App Here</Strong></Title>
<Header><Menus /></Header>
<Body><MyAppContent></Body>
</App>
)
}
This makes for a far better declarative way of using your component as they dont need to worry about structuring it but only declaring sections
Contributing
You can submit a propasal if you have a container you wanted to contribute.