当在数组.map内部分配时,Javascript onClick不触发

4
我已经创建了一个动态加载内容的网页,使用React.js。我通过REST api调用获取一个对象数组,然后将它们输入到表格中。我的问题是,数组.map内的onClick赋值没有触发指定的函数。
我认为这是一个[this]上下文问题,但我不知道如何解决。数组.map中的[this]与外部的[this]不同,这可以通过console.log证明。
我创建了两个独立的html文件进行演示。第一个包含一个静态创建的对象,正确地调用了onClick函数:

https://jsfiddle.net/m1vugyd9/

第二个尝试从对象数组中动态加载,但无法正常工作:

https://jsfiddle.net/avmbdxte/

我不知道这些链接的有效期是多久,所以如果它们失效了,我也会在下面包含实际的HTML代码。两者之间的差异在注释中有所突出。

静态 - 可用:

<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>

动态-不起作用:

<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>

工作示例(感谢pvg!):

https://jsfiddle.net/qLp9uuq3/

另一个工作示例(感谢Matthew Herbst!):

https://jsfiddle.net/09n6xss2/


https://dev59.com/3XM_5IYBdhLWcg3wiDqA 和许多其他问题。 - pvg
谢谢!我已经阅读了那篇文章和其他很多篇。我尝试过实现它们,但就是想不出该怎么做。我也读到过绑定(bind)存在问题? - Cam
如果您对bind感到困惑,您只需要将“this”设置为函数定义之外的某个其他变量,并在函数内部使用该变量即可。 “this”的行为是特殊的,其他所有内容都按预期行为。 - pvg
太好了,谢谢!尝试一下... - Cam
那个可行了,谢谢pvg!你想回答这个问题吗,这样我就可以给你积分了吗? - Cam
没问题,很高兴你的东西能够正常工作。这是一个重复的问题,可能不需要另外500个答案。 - pvg
1个回答

9
在您代码的动态部分中:Map()默认情况下不会从组件获取this的值,因此需要绑定它:
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));

如果您使用ES6箭头函数,this将会被词法继承,您就不会遇到这个问题了。
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转换。这是学习未来的好方法!


未捕获的类型错误:maps.map(...).bind不是函数? - Cam
抱歉,bind 必须在内部函数上使用,而不是在 map 上。我已更新答案。 - Matthew Herbst
这实际上是一个风格问题。var _this = thisbind(this) 都可以给你相同的能力。我个人更喜欢 bind,因为这意味着我不需要创建变量来绕过 JS 的限制。正如我在我的回答中提到的,开始转向 ES6,特别是在做 React 工作时,我认为这是最好的解决方案。这样你就可以获得 ES6 的优势,而且在它成为标准之前就开始学习了。大多数 React 插件都在 ES6 中完成所有示例,React 也只是时间问题。 - Matthew Herbst
2
这里有一个地图选项 map(function(){}, this); - J. Mark Stevens

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接