1. ホーム
  2. javascript

[解決済み] React, Uncaught RangeError: 最大呼び出しスタックサイズを超えている

2022-02-18 01:23:29

質問

私はreactで仕事をしていますが、基本的に私はツールチップを持つボタンを作りたいと思っています。マウスの入出時に見えるようにするために、cssのdisplayプロパティを変更しています。しかし、エラーが発生し、どうしたらいいかわかりません...。

以下は私のコードです。

import React from 'react';
import ReactDOM from 'react-dom';
import Style from 'style-it'; 
var Ink = require('react-ink');
import FontIcon from '../FontIcon/FontIcon';

var IconButton = React.createClass({ 

  getInitialState() {
      return {
          iconStyle: "",
          style: "",
          cursorPos: {},
      };
  },

  extend(obj, src) {
      Object.keys(src).forEach(function(key) { obj[key] = src[key]; });
      return obj;
  },

    Tooltip(props) {

        var style = {};

        if (this.tooltipDisplay) {
            style.display = "block";
        } else if (!this.tooltipDisplay) {
            style.display = "none";
        };

        return <div className="tooltip" style={style}>{_props.tooltip}</div>;
    },

    showTooltip(){
        this.tooltipDisplay = true;
    },

    removeTooltip(){
        this.tooltipDisplay = false;
    },

  render() {

    var _props = this.props,
      tooltip = this.Tooltip,
      opts,
      tooltipDisplay = false,
      disabled = false,
      rippleOpacity,
        outterStyleMy = {
        border: "none",
            outline: "none",
            padding: "8px 10px",
        "background-color": "red",
        "border-radius": 100 + "%",
        cursor: "pointer",
        },
        iconStyleMy = {
            "font-size": 12 + "px",
            "text-decoration": "none",
            "text-align": "center",
            display: 'flex',
            'justify-content': 'center',
            'align-items': 'center',
        },
      rippleStyle = {
        color: "rgba(0,0,0,0.5)",
      };

    if (_props.disabled || _props.disableTouchRipple) {
      rippleStyle.opacity = 0;
    };

    this.setState({
      iconStyle: _props.iconStyle
    });

    this.setState({
      style: _props.style
    });

    if (_props.disabled) {
       disabled = true;
    };

    if (this.state.labelStyle) {
        iconStyleMy = this.state.iconStyle;
    };

    if (this.state.style) {
      outterStyleMy = this.state.style;
    };

    if (_props.href) {
      opts.href = _props.href;
    };

        var buttonStyle = this.extend(outterStyleMy, iconStyleMy);

        return(
        <Style>
        {`
          .IconButton{
            position: relative;
          }
          .IconButton:disabled{
            color: ${_props.disabledColor};
          }
          .btnhref{
            text-decoration: none;
          }
        `}
         <a {...opts} className="btnhref" > 
          <tooltip text={this.props.tooltip} position={this.options} />
          <button ref="button" className={"IconButton" + _props.className} disabled={disabled} style={buttonStyle}
          onMouseEnter={this.showTooltip} onMouseLeave={this.removeTooltip} >
            <Ink background={true} style={rippleStyle} opacity={rippleOpacity} />
            <FontIcon className={_props.iconClassName}/>
          </button>
        </a>
        </Style>
        );

  }
});



ReactDOM.render(
 <IconButton href="" className="" iconStyle="" style="" iconClassName="face" disabled="" disableTouchRipple="" tooltip="aaaaa" />,
 document.getElementById('app')
);

コンソールでは、このエラーが表示されます。

Uncaught RangeError: Maximum call stack size exceeded
at defineRefPropWarningGetter (App.js:1053)
at Object.ReactElement.createElement (App.js:1220)
at Object.createElement (App.js:3329)
at Constructor.render (App.js:43403)
at App.js:15952
at measureLifeCyclePerf (App.js:15233)
at ReactCompositeComponentWrapper._renderValidatedComponentWithoutOwnerOrContext (App.js:15951)
at ReactCompositeComponentWrapper._renderValidatedComponent (App.js:15978)
at ReactCompositeComponentWrapper._updateRenderedComponent (App.js:15902)
at ReactCompositeComponentWrapper._performComponentUpdate (App.js:15880)

何が問題なのかがわからない。ある関数を呼び出すと、それが別の関数を呼び出すというようなことなのかもしれないとは思っています。しかし、私のコードにはこのようなものは見当たりませんし、それがすべてなのかどうかもわかりません。助けてくれてありがとうございます :)

解決方法は?

問題は、あなたが setState をレンダー関数の内部から実行します。状態の変更は、ユーザーがボタンをクリックした、ブラウザ ウィンドウのサイズが変更された、写真が撮影されたなど、何かが変更された場合にのみ発生するはずです。

レンダリング中に状態を更新することは絶対にしないでください(この最後の文章を20回繰り返して、絶対に忘れないでください)。

以下は問題のコードです。

render () {
    ...
    this.setState({
      iconStyle: _props.iconStyle
    });

    this.setState({
      style: _props.style
    });
    ...
}

上記のコードでは、無限ループのようなものが発生します。 setStaterender が呼び出されます。このため iconStylestyle はプロップであり、プロップは変更できないので、これらのプロップを使用して初期状態を構築する必要があります。

getInitialState() {
  return {
      iconStyle: this.props.iconStyle,
      style: this.props.style,
      cursorPos: {},
  };
}

その後、誰かがボタンをクリックして iconStyle を変更したい場合、状態を更新するクリックハンドラを作成します。

handleClick () {
  this.setState({
    iconStyle: 'clicked'
  });
}

そうすると、コンポーネントが再レンダリングされ、新しい状態が反映されます。

誰かが料理をしていて、その様子を写真に撮ると考えてみてください。その 初期状態 は"卵を割ったものです。 いいえ , 小麦粉を注いだ。 いいえ , 野菜のみじん切り。 いいえ "、この状態を写真に撮るのです。すると、シェフが何かをする - 卵を割る。今度は状態が変わったので、それを写真に撮ります。次に、シェフが野菜を切ります。このときも、状態が変化しているので、写真を撮ります。

この例えの各写真は、あなたのレンダリング機能、つまりある時点の状態のスナップショットを表しています。もし、写真を撮るたびに小麦粉が降り注いだら、小麦粉が降り注いだばかりなので、また写真を撮らなければなりません。また写真を撮れば、さらに小麦粉が注がれるので、また写真を撮らなければなりません。結局、セリアックの悪夢でキッチンが天井までいっぱいになり、部屋にいる全員が窒息死することになる。フィルムやカメラのハードディスクの容量も足りなくなる。