Javascript onClick does not work when assigned inside array.map

I created a web page that dynamically loads its contents using React.js. I extract an array of objects from a REST api call and then load them into a table. The problem is that assigning onClick from array.map does not call the assigned function.

I believe this is a [ this ] problem , but I'm not sure how to solve it. [ this ] in array.map is not the same [ this ] outside array.map, as shown in console.log.

I created two standalone html files for demonstration. The first contains a statically created object that correctly calls the onClick function:

https://jsfiddle.net/m1vugyd9/

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

https://jsfiddle.net/avmbdxte/

I have no idea how long these links remain active, so if they don't work, I also included the actual html below. The difference is somewhat emphasized by the 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 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 - does not 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 to Matthew Herbst!):

https://jsfiddle.net/09n6xss2/

+4
source share
1 answer

In the dynamic part of your code: Map()does not get the value thisfrom the default component, 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 the ES6 arrow functions, thisyou would be legally inherited and you would not have this problem:

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} />
  );
});

, , , Babel - JSX, , ES6. !

+7

Source: https://habr.com/ru/post/1621321/


All Articles