1

I know there is already a ton of questions same as mine (like this one.. ) But I could not find the perfect answer.

What i have to do

React v.15.6.2

I have to render several elements ordered by categories with JQuery Accordion, like in the image below :

Accordion look

Here is my render function :

public render():React.ReactElement<{}>{
  return (
    <div className={styles.extensionContainer}>
    /* TONS OF DIV WITH CONTENT*/
    <div className="accordion">
       {this.state.items.map((item:CategoryArray)=>
          <h3>{item.CategoryName}</h3>
          <div className={styles.answers}>
              {item.Items.map((x)=> 
                  <h3>{x.ItemTitle}</h3>
                  <div className={styles.answer} dangerouslySetInnerHTML={{__html: x.ItemAnswer}}></div>
               )}
          </div>
        )}
    </div>
    </div>

  );
}

But unforutnately it is not working, and the build throws me the following errors :

error TS2657 : JSX expressions mus have one parent element. error TS2695 : Left side of comma operator is unused and has no side effects

What I tried :

  1. At first, I was wrapping my first .map() results in a <div className="accordion">, but my jQuery Accordion widget got a little bugged because of that. That made me search for another solution.
  2. I tried to wrap it only with a simple <div> but it is absolutely not rendering my accordion correctly.
  3. I tried to wrap it in <> and </> (as said in the answers of this thread
  4. I even tried to wrap it with <Fragment></Fragment> by installing Babel-cli and importing fragment, but there is no chances in that either.

And now this leaves me with no more solutions. Not rendering well, or absolut bug, do you have any idea on How to render my accordion without wrapping every single item in a div , please ?

Thanks a lot ! Have a nice day!

Gaelle
  • 614
  • 1
  • 7
  • 30
  • You tried this https://codesandbox.io/s/j7857nkj89 ? – Arup Rakshit Aug 16 '18 at 12:44
  • You can use [`React.Fragment`](https://reactjs.org/docs/fragments.html) for this. – Tholle Aug 16 '18 at 12:48
  • There's no way of doing that without `Fragment`, but that requires upgrading to React ^16.0. What's your version? – naffiq Aug 16 '18 at 12:56
  • i think you have a typo `{item/CategoryName}` – madalinivascu Aug 16 '18 at 12:58
  • @ArupRakshit : Yes I already tried and could not make it work (which module do I have to import?) and your sandbox throws an error too. – Gaelle Aug 16 '18 at 14:03
  • @Tholle : thx too, please tell me which modul I have to install, because i've tried using React.Fragment and babel-cli, but it did not work. – Gaelle Aug 16 '18 at 14:03
  • @naffiq : You're right, i'm under 15.6.2... I'll try updating it first – Gaelle Aug 16 '18 at 14:04
  • @madalinivascu : Thanks, i'll edit the question, it's a typo ;) – Gaelle Aug 16 '18 at 14:04
  • The question should clearly state that it's React 15 because this is not something that goes without saying and is real problem here. Usually you use Fragment. It can be polyfilled in 15, https://github.com/benwiley4000/react-dot-fragment . Otherwise it's like the answer says. – Estus Flask Aug 16 '18 at 14:24
  • @estus : My bad, I forgot the React version, it's now done. I've tried using react-dot-fragment byt running `npm install react-dot-fragment`, and then `npm install @types/react-dot-fragment`, then `import Fragment from 'react-dot-fragment'`; and wrapping my first `.map()` 's content with `` but this time I get a react error when running local : > Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got : undefined. CHeck the render method. – Gaelle Aug 16 '18 at 14:51

2 Answers2

2

The problem is specific to React 15. An array of components can be returned from render to skip <div> wrapper, as another answer explains.

React 16 features <> and Fragment components that allow to group several components. <> is not supported by all tools, while Fragment can be polyfilled in React 15.

Here's an example:

import { Fragment } from "react-dot-fragment";

...

  <div className="accordion">
    {this.state.items.map((item: CategoryArray) => (
      <Fragment>
        {console.warn(item)}
        <h3>{item.CategoryName}</h3>
        <div>
          {item.Items.map(x => (
            <Fragment>
              <h3>{x.ItemTitle}</h3>
              <div dangerouslySetInnerHTML={{ __html: x.ItemAnswer }} />
            </Fragment>
          ))}
        </div>
      </Fragment>
    ))}
  </div>
);
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
0

You can use a array

  {this.state.items.map((item:CategoryArray)=> {
    return [<h3>{item.CategoryName}</h3>,
    <div className={styles.answers}>
          {item.Items.map((x)=> {
              return [<h3>{x.ItemTitle}</h3>,
              <div className={styles.answer} dangerouslySetInnerHTML={{__html: x.ItemAnswer}}></div>];
           })}
    </div>];
  })}

demo

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
madalinivascu
  • 32,064
  • 4
  • 39
  • 55
  • Hi @madalinivascu ! Thx for your answer, but I can't use a return within a return, can I ? – Gaelle Aug 16 '18 at 13:53
  • @Gaelle It's not within `return`. It's within `map` function. – Estus Flask Aug 16 '18 at 14:11
  • yep, but it still won't work : `{this.state.items.map((item:CategoryArray)=> return [

    {item.CategoryName}

    ....` I got several errors : TS1005 : '{' expected. error TS2657 JSX expressions must have one parent element; error TS1128 : Declaration or statement expected and TS2695.
    – Gaelle Aug 16 '18 at 14:59
  • @Gaelle It's either `{this.state.items.map((item:CategoryArray)=> [...])` or `{this.state.items.map((item:CategoryArray)=> { return [...] })`. The answer initially missed brackets, even though a demo was ok. – Estus Flask Aug 16 '18 at 15:15