Dynamic composition reactjs - reactjs

Is there a way of composition of dynamic component? For example, lets say I would a have a ListItems component that display its children in rows. But i want to reuse some of its behavior and I would like to to something like that:
<ListItems rowComponent="CarsItem" headerComponent="CarsHeader"/>
or
<ListItems rowComponent="BikesItem" headerComponent="BikesHeader"/>
How can I achieve such abstraction?
Somethin like that did not work
render:function(){
return (
<this.props.header/>
<this.props.body/>
)
}

If you can make sure you declare the class that's used for the children first, you can do something like this:
<ListItems rowComponent={BikesItem}/>
and then in your render method:
render() {
var Row = this.props.rowComponent;
return (
<div>
{this.props.items.map(function(item) {
return <Row item={item}/>;
})}
</div>
)
}
Hope this helps!

Related

Is it possible to add data to a React component using the same prop label more than once, then have the component map that data into list items?

I'd like to create a reusable component in React that displays a list of what payment methods are available for a certain service.
Is it possible to add the data to the component like this (with multiple uses of the same prop label)?
const Page = () => {
<PaymentMethods method="BACS Transfer" method="Cheque" method="PayPal" method="Credit / Debit Card" />
}
Or perhaps it's an array method=[BACS Transfer, Cheque]
Either way, I'd like to know if it's possible. If so, what does the component look like to map over the data and output as <li> items.
If this is a bad approach, what is the best way to do it?
Thanks in advance!
Pass them as an array, like this:
<PaymentMethods methods={["BACS Transfer", "Cheque", "PayPal", "Credit / Debit Card"]} />
The component itself should be something such as this:
const PaymentMethods = ({methods}) => {
return <ul>
{methods.map((method, index) => <li key={index}>{method}</li>)}
</ul>
}
There are many ways to achieve it.
put the items on one prop
<PaymentMethods
lists={['BACS Transfer', 'Cheque', 'PayPal', 'Credit/Debit Card']}
/>
////
function PaymentMethods({lists}){
return (
<ul>
{lists.map((text)=>(<li>{text}</li>))}
</ul>
)
}
Using children
<PaymentMethods>
<li>BACS Transfer</li>
<li>Cheque</li>
<li>PayPal</li>
<li>Credit/Debit Card</li>
</PaymentMethods>

Use a React element from a dynamically name (string)

I'm trying to dynamically change an element name for reuse of a function.
static renderDetails(props, parentTableElementOpen, parentTableElementClose, ) {
let coverageRows;
if (props.length !== 0) {
return (
<span>
{parentTableElementOpen}
{props.map((columnData, index) => {
if (index === props.length - 1) {
coverageRows = (<TableRowCol classNames={styles.text_align_right}>{columnData}</TableRowCol>);
}
else {
coverageRows = (<TableRowCol>{columnData}</TableRowCol>);
}
return coverageRows;
})}
{parentTableElementClose}
</span>
);
}
return null;
}
The call to this function is below.
Utils.renderDetails(this.props.columnData, '<TableRow>', '</TableRow>');
The parentTableElementOpen and parentTableElementClose will have the names of the elements I'm after.
The rendered page doesn't seem to recognize them and instead of a <TableRow> </TableRow> element type it renders just text <TableRow> </TableRow>
Maybe a bit tricky or overly complicated what I'm trying to do here but thought it could be a good refactor between 2 identical functions.
There might be a solution that actually could work in the way you described but I think you're thinking of this using an HTML mindset. Keep in mind that with React you're rendering a Component which is not an HTML Tag/XML even though it shares similarities with the syntax.
In your case you're passing a string so it is rendering a string.
I think what you want is a generic component that renders the children, not a function that tries to pick a component. Maybe something like this:
class MyTableRow extends React.Component {
render() {
return ( //do whatever customization you want here.
<TableRowCol>
{this.props.children} //renders what's "inside" the tag. You can pass this or specify it in the JSX
</TableRowCol>
)
}
}
If you think about what you're doing in that utility call you're actually specifying the tag you want to use and then just passing props which in the world of React is identical to:
//Some render function
...
<MyUtilityObject props={props} />
...
My first instinct would be to "invert" the design to use components as that seems to be how React is designed.
EDIT
I didn't realize that element.props.children was readonly so the idea below isn't going to work.
My suggestion in this case would be as above. In general this abstract method really isn't doing anything and could be refactored into a custom component so instead of a function call you use the component
<MyTable>
{ row.map( (row, index) => {
switch(row.type) {
case 'data1':
return <MyCustomRow row={row} key={index} />
case 'data2':
return <MyCustomRow2 row={row} key={index} />
default:
return null
}
})
</MyTable>
NOPE
Now that being said, if you wanted to maintain this signature and you have a good reason what you probably want to do is this:
static renderDetails(props, parentElement) {
if(props.length === 0) {
return null; //nothing to see here!
}
let coverageRows;
let children = props.map((columnData, index) => {
if (index === props.length - 1) {
coverageRows = (<TableRowCol classNames={styles.text_align_right}>{columnData}</TableRowCol>);
}
else {
coverageRows = (<TableRowCol>{columnData}</TableRowCol>);
}
return coverageRows;
})
parentElement.children = children
return <span>parentElment</span> //I'm not sure why you need the span, but if you do great. I would just return the parentElement
}
//Called by...
render() {
...
renderDetails(props, <TableRow />)//an actual table row instance, not the tag name as a string
...
}
I didn't test any of this but it should get you moving in the right direction. I would recommend writing a custom component that renders children so you understand how that works. It will save you a lot of time down the road.

Conditionally render list with Higher Order Component

My app has a feature toggle functionality that tells my UI whether or not a piece of UI should be rendered. I would like to create a Higher Order Component to conditionally render these types of components. In one scenario, I'm trying to conditionally render a list, but I'm running into this error:
ConditionalRender(...): A valid React element (or null) must be returned. You may have returned undefined, an array or some other invalid object.
This makes sense since I just am trying to render the children of this component. Here's what I've got so far:
https://jsfiddle.net/fmpeyton/cykmyabL/
var settings = { showHello: true, showGoodbye: false};
function ConditionalRender (props) {
var output = null;
if(props.shouldRender) output = props.children;
console.log(output);
// return (<li>{output}</li>); // This works, but isn't desired html structure
return ({output});
}
function App (props) {
return (
<ul>
<ConditionalRender shouldRender={props.settings.showHello}>
<li>Hello!</li>
</ConditionalRender>
<ConditionalRender shouldRender={props.settings.showGoodbye}>
<li>Goodbye...</li>
</ConditionalRender>
</ul>
);
}
ReactDOM.render(
<App settings={settings} />,
document.getElementById('container')
);
If I can help it, I would just like to render the children without any additional logic.This HOC would also handle more complex children down the line. Something like this:
<ConditionalRender shouldRender={props.settings.showHello}>
<div>
<p> blah blah blah</p>
<table>
<!-- ... -->
</table>
</div>
</ConditionalRender>
Any ideas?
Try this:
function App(props) {
return (
<ul>
{props.settings.showHello && <li>Hello!</li>}
{props.settings.showGoodbye && <li>Goodbye...</li>}
</ul>
);
}
P.S. Your code doesn't work because of this line:
return ({output});
Assuming you have es2015 support, it would be treated as object property shorthand. So it's the same as:
return {output: output};
which is not what React expects to get from the render method.
You could try this:
function ConditionalRender(props) {
if (props.shouldRender) {
// Make sure we have only a single child
return React.Children.only(props.children);
} else {
return null;
}
}
But this is not the simplest way. See more here.
P.P.S. Your ConditionalRender component is not what is called Higher-Order Component. According to the docs, HOC is a function that takes a component and returns a new component.

How to reuse React component that has different markup?

I have the following component today, which represents a container box that has a heading and different rows. This is how it looks:
var Box = React.createClass({
render: function() {
return (
<BoxHeading title={this.props.headingTitle}/>
<BoxBody rows={this.props.rows} />
)
}
});
var BoxBody = React.createClass({
render: function() {
return (
{this.rows()}
)
}
rows: function() {
return _.map(this.props.rows, function(row) {
return (
<HouseRow />
);
}, this);
}
});
Now my question is: If I want to reuse the Box & BoxBody but instead of using I want to use another kind of Row, how would I do it?
Would I pass the kind of component that I want as row to the Box?
So, I would do <Box rowType={<HouseRow} /> or something similar?
The approach you chose looks really good — you can basically combine Box with every type of row you want, provided it has a correct interface. It is called Object Composition and is a legit and well respected pattern in software engineering.
The only thing is, in React you should do it not by passing a already rendered component, but the component class, like this:
<Box rowComponent={ HouseRow } />
As you see, you don't need to use parenthesis. And inside component, you do something like this:
var RowComponent = this.props.rowComponent;
return (
<RowComponent />
);
<Box rowType={HouseRow} />
This is exactly how you would do it. Pass dynamic content that varies from components as props from a parent. Then in your rows function, return <{#props.rowType} /> should work

How to pass a state className variable to another component in react

I'm not sure if this is the best way to do things but i want to pass one class name variable to another component in react.
This is a button which launch an animation onClick just adding one className to few elements of it. Also i created the var = overlay this.state.cliked ? 'open' : '' to launch an overlay, if i have the overlay html on the same component it works fine but i have to do little components as i can.
var React = require('react');
var OverlayView = require('./OverlayView.jsx');
var StatusBarButtonView = React.createClass({
getInitialState: function() {
return {cliked: false};
},
handleClick: function(event) {
this.setState({cliked: !this.state.cliked});
},
render: function() {
var fondo = this.state.cliked ? 'active' : '';
var overlay = this.state.cliked ? 'open' : '';
return (
<div>
<div className={"statusbar-button-container " + (fondo)} onClick={this.handleClick}>
<img src="images/navbar-element-icon-cross.png" className={"rotate " + (fondo)}/>
</div>
</div>
<OverlayView className={overlay} />
);
}
});
module.exports = StatusBarButtonView;
As you see the is the component of the overlay i want to pass to this component but im not sure if it can just be alone and be launched when this one handle the click. im a bit lost with react, not so much online info and im new with this.
This is the Overlay component:
var React = require('react');
var OverlayView = React.createClass({
return (
<div className={"overlay overlay-slidedown " + this.props.class}>
<nav>
<ul>
<li>Home</li>
<li>About</li>
<li>Work</li>
<li>Clients</li>
<li>Contact</li>
</ul>
</nav>
</div>
);
}
});
module.exports = OverlayView;
I'm not sure how to do this, im looking for examples around the web but nothing very clear for me :(
Use this:
<div className={`statusbar-button-container ${fondo}`} onClick={this.handleClick}>
Note: Make difference between ' and ` (known as backticks). This sign on keyboard is just left to 1 and above tab.
Passing class names as strings between components and even appending class names in the same component is error-prone and not ideal. I'd suggest using the classSet() helper: https://facebook.github.io/react/docs/class-name-manipulation.html
In your case, instead of passing a class prop to the OverlayView component, you should ideally pass a prop that describes the state of the component. Within the OverlayView component, compute the correct classes to be applied using the classSet helper.
For example, instead of using this:
<OverlayView className={overlay} />
you could simply pass in the state variable:
<OverlayView isOpen={this.state.cliked} />
In your OverlayView component, you would then create a classes object using the className helper:
var classes = cx({
'overlay': true,
'overlay-slidedown': true,
'open': this.props.isOpen
});
And change the line in your render() function to use the classes object:
...
<div className={classes}>
...
I tried this part of your code...
return (
<div className={"overlay overlay-slidedown " + this.props.class}>
);
And it seemed to work perfectly for me. It solved my problem: a prop failing to interpolate when I want to display it next to some static text.
I find this better than the accepted answer, because that solved the problem by parameterizing the extra concat'd values, when this is often not desired and against general encapsulation philosophy.
I find it much neater to place the classes in an array, and then reference that:
const styles = [
"container",
"px-4",
"h-1/3",
this.props.class
]
return (
<div className={styles.join(" ")}>
Hello!
</div>
)

Resources