1

I'm trying to use react-rails to create a UI for an existing rails app. I want to make a dynamic todo list which is populated with data from my rails backend. I'm passing the tasks to the component through an erb tag provided by the gem 'react-rails'. This should make the data available as a prop for me to use, but when I try to render it in the JSX, I get an

 Uncaught TypeError: Cannot read property 'tasks' of undefined

error message in the console for the line

 for (var i = 0; i < this.props.tasks.length; i++) {

Here's my code:

The React(rails) component

class TodoApp extends React.Component{

  render() {
    return (
      <div className="card col-6">
        <div className="card-header">
          <div className="float-rights">
            create new button
         </div>
        </div>
       <div className="card-body">
          {
            (function(){
              var html;
             for (var i = 0; i < this.props.tasks.length; i++) {
                html += this.props.tasks[i]
              }
             return html;
            })()
         }
        </div>
     </div>
    )
  }
 }

The ERB/HTML view it's rendered in

 <%= react_component('TodoApp', tasks: @collaboration.tasks) %>

Ideally the loop would run and I would get some output.

bflemi3
  • 6,698
  • 20
  • 88
  • 155
iThompkins
  • 155
  • 1
  • 12
  • 1
    Why exactly do you need an iife inside JSX? You can use the array `map`, also, in order to directly set html, you have to use a special syntax, you might want to consider sending json from the server. – Avin Kavish Jun 21 '19 at 17:42
  • 2
    Are you sure `this` inside of the iifee is not undefined? It looks to me like it it should be undefined, I would use an arrow function to capture lexical this. – Amauri Jun 21 '19 at 17:44
  • What is the point of an IIFE here? – John Ruddell Jun 21 '19 at 17:52
  • @JohnRuddell Primarily as a container for the loop. After reading through some of these answers (@AvinKavish and @Amauri) I see that it's a bit unnecessary and that I should just use an array method. – iThompkins Jun 21 '19 at 18:30
  • 1
    Yep! i'd recommend going through [**react docs for lists and keys**](https://reactjs.org/docs/lists-and-keys.html) to get more info about best practices related to what you're trying to do :) – John Ruddell Jun 21 '19 at 18:32
  • Thanks @JohnRuddell, will do! – iThompkins Jun 22 '19 at 20:28

2 Answers2

2

Your IIFE is a closure, so this is not the context you think it is. Try this...

<div className="card-body">
    {this.props.tasks.map(task => task)}
</div>

Although I'm not sure what task is... that's another problem you may need to fix. If you provide an explanation I will update my answer appropriately.

bflemi3
  • 6,698
  • 20
  • 88
  • 155
2

You could get your IIFE working by binding this from the outer scope,

<div className="card-body">
    {
        (function(){
             var html;
             for (var i = 0; i < this.props.tasks.length; i++) {
                html += this.props.tasks[i]
              }
            return html;
         }).bind(this)()
     }
</div>

But this is not idiomatic React. The following is,

<div className="card-body">
    {this.props.tasks.join('')}
</div>

If the tasks are in html format as indicated by the name of the variable you have used,

<div className="card-body"
     dangerouslySetInnerHTML={ { __html: this.props.tasks.join('') } }>
</div>

With this approach you have to sanitize the tasks to prevent script injection and you also lose the benefits of react such as event binding. It is not recommended

Avin Kavish
  • 8,317
  • 1
  • 21
  • 36
  • Good answer @AvinKavish. Much more thorough explanation than mine :) – bflemi3 Jun 21 '19 at 18:46
  • 1
    @bflemi3 yours also answered the core of my question, but I will give the nod to Avin incase someone else wants the depth of response. – iThompkins Jun 22 '19 at 20:28