Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React JSX conditional wrapper for bootstrap grid

Just face this funny thing while rendering React components with Bootstrap grid, assuming I need to render two col-md-6 in a row, each <Book />component is put inside a col-md-6 div block

+---------------------+
| col-md-6 | col-md-6 |
| Book     | Book     |  <--- row
|---------------------|
| col-md-6 | col-md-6 |
| Book     | Book     |  <--- row
+---------------------+

and it expects to form similar to the below structure,

<Row>
    <Col>
        <Book />
    </Col>
    <Col>
        <Book />
    </Col>
</Row>
<Row>
    <Col>
        <Book />
    </Col>
    <Col>
        <Book />
    </Col>
</Row>

What I tried to render like following,

export default class BookList extends React.Component {
    render() {
        let books = [];
        lodash.each(this.props.books, function (book, index) {
            index % 2 === 0 ? books.push(<div className="row" key={index}>): null;
            books.push(
                    <div className="col-md-6">
                        <Book />
                    </div>
            )
            index % 2 === 1 || index === this.props.books.length - 1? books.push(</div>): null
        })

        return (
            <div className="container">
                {books}
            </div>
        )
    }
}

But the output is a malformed HTML, it looks like JSX validate each item on render, and not wait for closing tag.

Is there any way or tip to make it work correctly?

like image 506
Pete Houston Avatar asked Mar 28 '16 18:03

Pete Houston


People also ask

What is conditional rendering in react?

Conditional Rendering. In React, you can create distinct components that encapsulate behavior you need. Then, you can render only some of them, depending on the state of your application. Conditional rendering in React works the same way conditions work in JavaScript.

How to conditionally include an element in react JS?

It can be handy for conditionally including an element: It works because in JavaScript, true && expression always evaluates to expression, and false && expression always evaluates to false. Therefore, if the condition is true, the element right after && will appear in the output. If it is false, React will ignore and skip it.

How does conditionalwrapper work in JavaScript?

The condition part is quite self-explanatory. Then the tricky part is the wrapper. It receives a function that will be called in the ConditionalWrapper component itself when the condition is true. There it will receive the implicit children as argument.

How do I conditionally render a component in JSX?

While declaring a variable and using an if statement is a fine way to conditionally render a component, sometimes you might want to use a shorter syntax. There are a few ways to inline conditions in JSX, explained below.


1 Answers

You've treated the JSX as strings instead of XML, and when it was compiled to JS, it was invalid.

React works well with small components, so break the BooksContainer into pairs and render the BooksRows, and the BooksRows will render the books (fiddle - inspect the result):

code:

const Book = ({ book }) => (
  <div className="col-md-6">
    { book.name }
  </div>
);

const BooksRow = ({ bookPair }) => (
  <div className="row">
    {
      bookPair.map((book, index) => (
        <Book key={ index } book={ book }/>
      ))
    }
  </div>
);

const BooksContainer = ({ books }) => (
  <div className="container">
    {
      books.reduce((pairs, book, index) => { // split the books into pairs
        if(index % 2 === 0) {
           pairs.push([]);
        }
        pairs[pairs.length - 1].push(book);
        return pairs;
      }, []).map((pair, index) => ( // map the pairs to row
        <BooksRow key={ index } bookPair={ pair } />
      ))
    }
  </div>
);

const books = [
  {
    name: 'cats'
  },
  {
    name: 'dogs'
  },
  {
    name: 'rabbits'
  },
  {
    name: 'elephents'
  },
  {
    name: 'snails'
  },
  {
    name: 'tigers'
  }
];

ReactDOM.render(
  <BooksContainer books={ books } />,
  document.getElementById('container')
);
like image 104
Ori Drori Avatar answered Oct 06 '22 16:10

Ori Drori