4

I have created a webpage that dynamically loads its content with React.js. I'm retrieving an array of objects from a REST api call, and then feeding them into a table. The issue I have is that the onClick assignment from within the array.map doesn't trigger the assigned function.

I believe this is a [this] context issue, but I'm not sure how to resolve it. The [this] in the array.map is not the same [this] outside of the array.map as demonstrated by console.log.

I have created two standalone html files for demonstration. The first contains a statically created object which is calling the onClick function correctly:

https://jsfiddle.net/m1vugyd9/

The second attempts to dynamically load from an array of objects, and does not work:

https://jsfiddle.net/avmbdxte/

I have no idea how long those links stay active for, so if they don't work, I've also included the actual html below. The difference is somewhat highlighted with comments.

Static - works:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style>
        table {
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="reactDiv"></div>

    <script src="https://fb.me/react-0.14.3.min.js"></script>
    <script src="https://fb.me/JSXTransformer-0.13.3.js"></script>
    <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script type="text/jsx">
        var FMap = React.createClass({
            render: function() {
                return (
                    <tr>
                        <td>{this.props.sTable}</td>
                        <td>{this.props.sField}</td>
                        <td>{this.props.dTable}</td>
                        <td>{this.props.dField}</td>
                        <td><a href="#" onClick={this.props.mapCountClick}>{this.props.mapCount}</a></td>
                    </tr>
                );
            }
        });

        var Main = React.createClass({

            getInitialState: function () {
                return { mapData: [] };
            },

            editMaps: function() {
                alert("Clicked on map editor");
            },

            render: function () {
                var maps = [
                    {
                        mapID: 1,
                        sourceT: "sT1",
                        sourceF: "sF1",
                        destT: "dT1",
                        destF: "dF1",
                        mapCount: 6
                    },
                    {
                        mapID: 2,
                        sourceT: "sT1",
                        sourceF: "sF2",
                        destT: "dT1",
                        destF: "dF2",
                        mapCount: 2
                    }
                ];



                /////////////////////////////////////////////////////
                // this is the static part that's different from the dynamic part
                var fMaps =
                    <FMap key="1"
                          sTable="sT1"
                          sField="sF1"
                          dTable="dT1"
                          dField="dF1"
                          mapCount="6"
                          mapCountClick={this.editMaps} />;
                // end of difference
                /////////////////////////////////////////////////////



                return (
                    <table width="100%">
                        <thead>
                            <tr>
                                <th>SourceT</th>
                                <th>SourceF</th>
                                <th>DestT</th>
                                <th>DestF</th>
                                <th>MapCount</th>
                            </tr>
                        </thead>
                        <tbody>
                            {fMaps}
                        </tbody>
                    </table>
                );
            }
        });

        React.render(<Main />, document.getElementById("reactDiv"));
    </script>
</body>
</html>

Dynamic - doesn't work:

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <style>
        table {
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="reactDiv"></div>

    <script src="https://fb.me/react-0.14.3.min.js"></script>
    <script src="https://fb.me/JSXTransformer-0.13.3.js"></script>
    <script src="http://code.jquery.com/jquery-1.11.3.min.js"></script>
    <script type="text/jsx">
        var FMap = React.createClass({
            render: function() {
                return (
                    <tr>
                        <td>{this.props.sTable}</td>
                        <td>{this.props.sField}</td>
                        <td>{this.props.dTable}</td>
                        <td>{this.props.dField}</td>
                        <td><a href="#" onClick={this.props.mapCountClick}>{this.props.mapCount}</a></td>
                    </tr>
                );
            }
        });

        var Main = React.createClass({

            getInitialState: function () {
                return { mapData: [] };
            },

            editMaps: function() {
                alert("Clicked on map editor");
            },

            render: function () {
                var maps = [
                    {
                        mapID: 1,
                        sourceT: "sT1",
                        sourceF: "sF1",
                        destT: "dT1",
                        destF: "dF1",
                        mapCount: 6
                    },
                    {
                        mapID: 2,
                        sourceT: "sT1",
                        sourceF: "sF2",
                        destT: "dT1",
                        destF: "dF2",
                        mapCount: 2
                    }
                ];



                /////////////////////////////////////////////////////
                // this is the part that doesn't work
                var fMaps = maps.map(function (map) {
                    var component = this;

                    return (
                        <FMap key={map.mapID}
                            sTable={map.sourceT}
                            sField={map.sourceF}
                            dTable={map.destT}
                            dField={map.destF}
                            mapCount={map.mapCount}
                            mapCountClick={this.editMaps} />
                    );
                });
                // end
                /////////////////////////////////////////////////////



                return (
                    <table width="100%">
                        <thead>
                            <tr>
                                <th>SourceT</th>
                                <th>SourceF</th>
                                <th>DestT</th>
                                <th>DestF</th>
                                <th>MapCount</th>
                            </tr>
                        </thead>
                        <tbody>
                            {fMaps}
                        </tbody>
                    </table>
                );
            }
        });

        React.render(<Main />, document.getElementById("reactDiv"));
    </script>
</body>
</html>

Working example (thanks pvg!):

https://jsfiddle.net/qLp9uuq3/

Another working example (thanks Matthew Herbst!):

https://jsfiddle.net/09n6xss2/

6
  • stackoverflow.com/questions/1338599/… and many others. Commented Dec 22, 2015 at 17:32
  • Thanks! I've read through that and many others. I've tried to implement them but just can't figure out how to do it. I've also read that bind is problematic? Commented Dec 22, 2015 at 17:36
  • if you are confused by bind, all you have to do is set this to some other variable outside of the function def and use that inside the function instead. the behaviour of this is special, everything else behaves as expected. Commented Dec 22, 2015 at 17:39
  • Great, thanks!! Giving it a go... Commented Dec 22, 2015 at 17:41
  • That worked, thanks pvg! Do you want to answer the question so I can give you credit? Commented Dec 22, 2015 at 17:49

1 Answer 1

9

In the dynamic part of your code: Map() doesn't get the value of this from the component by default, so you need to bind it:

var fMaps = maps.map(function (map) {
  return (
    <FMap key={map.mapID}
      sTable={map.sourceT}
      sField={map.sourceF}
      dTable={map.destT}
      dField={map.destF}
      mapCount={map.mapCount}
      mapCountClick={this.editMaps} />
  );
}.bind(this));

If you used ES6 arrow functions, this would be lexically inherited and you wouldn't have this issue:

var fMaps = maps.map((map) => {
  return (
    <FMap key={map.mapID}
      sTable={map.sourceT}
      sField={map.sourceF}
      dTable={map.destT}
      dField={map.destF}
      mapCount={map.mapCount}
      mapCountClick={this.editMaps} />
  );
});

Considering you are likely already using Babel or some other tool for JSX transform, might be worth looking into doing ES6 transforms as well. Good way to start learning the future!

Sign up to request clarification or add additional context in comments.

4 Comments

Uncaught type error: maps.map(...).bind is not a function?
Oops sorry, the bind has to be on the inner function, not on the map. I updated the answer
It's really a matter of style. var _this = this and bind(this) will give you the same capabilities. I personally prefer bind since it means I'm not creating variables just to get around JS limitation. As I mentioned in my answer, starting to move to ES6, especially when doing React work, is the best solution I think. That way you get the advantages of ES6 plus you start to learn it before it becomes standard. Most React add-ins do all their examples in ES6, only a matter of time before React does too.
And there is the map option map(function(){}, this);

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.