24
{props.stories.map((story) =>
    <div key={story.id}>
        {story = story.story}
        <SliderItem story={story} />
    </div>
)}

The above code throws an error saying:

Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys

because story in line number 3 is an object.

I can move that condition directly as <SliderItem story={story.story} />, but I want to know if there is any way I can assign my calculated value to a variable and use it in JSX?

I want to apply some logic like:

{ let storyObj = story.type === 'story' && story.story ? story.story : story }
Lauren Rutledge
  • 1,195
  • 5
  • 18
  • 27
Asim K T
  • 16,864
  • 10
  • 77
  • 99

6 Answers6

28

Everything between {} in the JSX is just JavaScript, so you can do the following:

{props.stories.map((story) => {
  const storyObj = (story.type === 'story' && story.story) ? story.story : story;
  return (
    <div key={story.id}>
      <SliderItem story={storyObj} />
    </div>
  );
})}
Tholle
  • 108,070
  • 19
  • 198
  • 189
  • 13
    I think the general desire here is to have a normal variable declaration inside jsx, when you don't have something like a .map call creating such an opportunity – Devin Rhode Nov 22 '19 at 19:51
  • @DevinGRhode - on the one hand, I agree. On the other hand, I see only two possibilities: a) you wish to create an expression based on existing JS variable(s), in which case there *is* a JS context that defined that variable. The only reason OP had trouble is they were using "short" form of `map`s return value, that lacked the explicit `{...}`. The fix was to convert to "long" form. b) You are defining a literal value, in which case any enclosing JS context works fine. BOTTOM LINE: "Add declaration to the tightest enclosing JS context" is always an available solution. – ToolmakerSteve Oct 20 '20 at 19:26
  • looks like very complicated and not straight forward. – Siwei Aug 13 '23 at 08:25
10

You can create a function outside the render() that will get correct value:

function getStory(storyObj) {
  return storyObj.type === 'story' && storyObj.story ? storyObj.story : storyObj;
}

And then use it in JSX:

{props.stories.map(storyObj =>
  <div key={storyObj.id}>
     <SliderItem story={getStory(storyObj)} />
  </div>
)}
dominik791
  • 692
  • 6
  • 17
6

I think this would be a fairly elegant solution:

function someReactFunctionalComponent(props) {
  const vars = {}

  return (
    <div>
      <h1>... lots of code ...</h1>
      {vars.someFoo_s = bar ? dance : undefined}
      <ul>
        {
          vars.someFoo=normalizeFoo(vars.someFoo_s),
          vars.someFoo_s.map((aFoo) => (
            <li>{aFoo}</li>
          ))
        }
      </ul>
    </div>
  )
}
Devin Rhode
  • 23,026
  • 8
  • 58
  • 72
3

Though I wouldn't recommend it as it really obfuscates your code, you can use the comma operator to allow you to assign a value:

{story = story.story, <SliderItem story={story} />}

I'm not sure why you would want to do this however?

Develer
  • 41
  • 2
  • 3
    Note that this wouldn't allow you to create/declare a new variable. So things like the following will not work: `{let foo = 123, }` – Qtax Jun 12 '20 at 09:22
3

You can use an IIFE(Immediately Invoked Function Expression)

{props.stories.map((story) =>
    <div key={story.id}>
        {(() =>  {
          story = story.story
          return <SliderItem story={story} />
        })()}
    </div>
)}
KMA Badshah
  • 895
  • 8
  • 16
0

Why don't you just pass in story.story??

  {props.stories.map((story) =>
    <div key={story.id}>
      <SliderItem story={story.story} fallback={story} />
    </div>
  )}

In your component if this.props.story is undefined then use this.props.fallback.

Chase DeAnda
  • 15,963
  • 3
  • 30
  • 41
  • 1
    No.. My component should accept both structures. I don't want my end users to pass both props. I may not be the person who is using this component. And let's say `SliderItem` only accepts `story` – Asim K T Oct 09 '17 at 06:28