0

Is it possible to compose more different parts of component to variable? documentation

const App = () => {
  let element;

  element = <View>
      <Text>text</Text>
    </View>  // --> OK

  element = element + <View>
      <Text>text2</Text>
    </View>  // --> TypeError: Cannot read properties of undefined (reading 'height')

  element.push(<View>
      <Text>text3</Text>
    </View>);  // --> TypeError: element.push is not a function

  return <>
    {element}
  </>
}

export default App;

I use reactjs 17.0.2, typescript and "@react-pdf/renderer": "2.3.0".

Elo
  • 226
  • 5
  • 19
  • 2
    The error is reasonable. you wanna push in jsx and it's not allowed. you can create an array and push all of your jsx into it and after that use "map" function to render what you want – Ehsan Sep 02 '22 at 08:01
  • Do you mean something like this? `let element = []; element[0] = ( text element );` Error: Type 'Element' is not assignable to type 'never'. – Elo Sep 02 '22 at 08:16
  • Probably something like this: `let element: any = []; element.push(text element);` No error. – Elo Sep 02 '22 at 08:34

1 Answers1

3

Update

Based on your question here, this should work:

<Document>
  <Page size="A4" orientation="landscape">
    {/* -- Table LEFT: -- */}
    <View>
      {/* -- Table Head: -- */}
      <View>
        <Text>Index</Text>
        <Text>Brand</Text>
        <Text>Type</Text>
      </View>
      {/* -- Table Body: -- */}
      {data?.cars?.length &&
        data.cars.map(({ id, brand, type }, index) => (
          <View key={`${id}-left`}>
            <Text>{index + 1}</Text>
            <Text>{brand || ''}</Text>
            <Text>{type || ''}</Text>
          </View>
        ))}
    </View>
  </Page>

  <Page size="A4" orientation="landscape">
    {/* -- Table RIGHT: -- */}
    <View>
      {/* -- Table Head: -- */}
      <View>
        <Text>Color</Text>
        <Text>Fuel</Text>
      </View>
      {/* -- Table Body: -- */}
      {data?.cars?.length &&
        data.cars.map(({ id, color, fuel }) => (
          <View key={`${id}-right`}>
            <Text>{color || ''}</Text>
            <Text>{fuel || ''}</Text>
          </View>
        ))}
    </View>
  </Page>
</Document>

The issue seems to be with how you're handling arrays, not with rending React elements.

If you want to access the properties of the object in an array element, you can destructure the element, so instead of

data.cars.map((car, index) => (<Text>{car.color}</Text>))

you can do

data.cars.map(({id, brand, type, color, fuel}, index) => (<Text>{color}</Text>));

If you're not performing any operations on the array elements, you can use an implicit return instead of an explicit return:

// explicit return
data.cars.map(({id, brand, type, color, fuel}, index) => { 
  // do something else here
  return (
    <Text>{color}</Text>
  )
});
// implicit return
data.cars.map(({id, brand, type, color, fuel}, index) => (<Text>{color}</Text>));

Also, when you're rending known text values in React, you don't need to wrap it in curly braces ({}), you can just render the text directly.

Instead of

<Text>{'color'}</Text>

you can just put

<Text>color</Text>

unless it's required by whatever library you're using. I'm not familiar with @react-pdf/renderer.

One more thing to keep in mind is that the key for list items in React should be something stable. Using array indices as keys is discouraged (see React docs).


Original answer

If you want to render an element this way, you could do something like this:

const App = () => {
  let element = [];

  // Each child in a list needs a unique "key" prop
  element.push(<View key={someUniqueKey}>
      <Text>text</Text>
    </View>)

  element.push(<View key={someOtherUniqueKey}>
      <Text>text2</Text>
    </View>)

  element.push(<View key={oneMoreUniqueKey}>
      <Text>text3</Text>
    </View>);

  return <>
    {element}
  </>
}

export default App;

Personally, I haven't seen anyone render components like this.

The strategy you are looking for is called conditional rendering, and there are different ways to do this depending on the situation.

For example, if you're trying to dynamically render data from an API response, you could do something like this:

const App = () => {
  const { data } = fetchDataFromAPI();

  return (
    <>
      <View>
        <Text>text</Text>
      </View>

      {data?.text2 && (
        <View>
          <Text>{data.text2}</Text>
        </View>
      )}

      {data?.text3 && (
        <View>
          <Text>{data.text3}</Text>
        </View>
      )}
    </>
  );
};

export default App;

You can check out the React docs for conditional rendering and rendering lists.

(Note: The above links are for the beta docs. If you prefer the classic(?) docs: conditional rendering and lists)

Larissa
  • 118
  • 2
  • 10
  • Thanks, I will try it. [This](https://stackoverflow.com/questions/73555575/table-split-to-two-parts-left-and-right-in-pdf-file) is why I am looking for such an unusual solution. – Elo Sep 02 '22 at 09:14
  • This code: `return ({element})` throws in Chrome: "paused before potential out-of-memory crash" How would I do it with map() function? `return ({element.map()})`, "TypeError: undefined is not a function". – Elo Sep 02 '22 at 09:54
  • Error: Cannot find name 'index' and 'value'. `return (<> element.map(function (value, index, array) ({value})>)`. What is wrong? – Elo Sep 02 '22 at 11:07
  • @Elo updated my answer based on your other question. Let me know if you're still experiencing issues. – Larissa Sep 02 '22 at 11:33