web-dev-qa-db-ja.com

ReactJSのネストされたコンポーネントでクラスを切り替える方法

ルートコンポーネントにそのようなrender()メソッドがあります。

render: function() {
    return (
        <div className="question">
            <QuestionA question={this.props.question} author={this.props.author}/>
            <QuestionB yes={this.state.yes} no={this.state.no} />
            <div className="question-side-switcher" onClick={this.handleSideChanging}></div>
        </div>
    );
}

ユーザーがボタンをクリックしたときに、「アクティブ」クラスをQuestionAコンポーネントとQuestionBコンポーネントの間で切り替えたいところ。これどうやってするの? QuestionAとQuestionBはrender()メソッドで独自のclassNamesが設定されていることに注意してください。たとえば、QuestionBのrender():

render: function() {
    return (
        <section className="question-b-container">
            ...
        </section>
    );
}
19
kirill.buga

これを処理する方法はいくつかあります。

親が追加のクラスを制御するようにしたい場合は、それを子コンポーネントに渡して、既存のクラス名に追加させるだけです( JSFiddle demo ):

var QuestionA = React.createClass({
  render: function() {
    return <section className={this.props.className + " question-a-container"}>Section A</section>;
  }
});

var QuestionB = React.createClass({
  render: function() {
    return <section className={this.props.className + " question-b-container"}>Section B</section>;
  }
});

var Root = React.createClass({
  getInitialState: function() {
    return { question: 'a' };
  },

  render: function() {
    var qAclassName = this.state.question === 'a' ? 'active' : '';
    var qBclassName = this.state.question === 'b' ? 'active' : '';
    return (
      <div className="question">      
        <QuestionA className={qAclassName} />
        <QuestionB className={qBclassName} />
        <div className="question-side-switcher" onClick={this.handleSideChanging}>Change</div>
      </div>
    );
  },

  handleSideChanging: function() {
    this.setState({question: this.state.question === 'a' ? 'b' : 'a' });
  }
});

ただし、おそらく、子にクラス名を管理させ、アクティブなクラスを設定する必要があるかどうかを示すためにデータを送信するだけの方が理にかなっています( JSFiddle demo ):

// Using classSet to more easily create conditional class names;
// see http://facebook.github.io/react/docs/class-name-manipulation.html
var cx = React.addons.classSet;

var QuestionA = React.createClass({
  render: function() {
    // always set "question-a-container";
    // set "active" if we got a truthy prop named `active`
    var className = cx({
      "question-a-container": true,
      "active": this.props.active
    });
    return <section className={className}>Section A</section>;
  }
});

var QuestionB = React.createClass({
  render: function() {
    // always set "question-b-container";
    // set "active" if we got a truthy prop named `active`
    var className = cx({
      "question-b-container": true,
      "active": this.props.active
    });
    return <section className={className}>Section B</section>;
  }
});

var Root = React.createClass({
  getInitialState: function() {
    return { question: 'a' };
  },

  render: function() {
    return (
      <div className="question">
        {/* For each question, compare to state to see if it's active. */}
        <QuestionA active={this.state.question === 'a'} />
        <QuestionB active={this.state.question === 'b'} />
        <div className="question-side-switcher" onClick={this.handleSideChanging}>Change</div>
      </div>
    );
  },

  handleSideChanging: function() {
    this.setState({question: this.state.question === 'a' ? 'b' : 'a' });
  }
});
22
Michelle Tilley

Brian Voelkercomments で言及しているように、Reactでクラスを操作する現在公式な方法は classnames ユーティリティ。

次のように2つのコンポーネントを定義できます。

import React, { Component } from 'react'
import classNames from 'classnames'

class QuestionA extends Component {

  render () {

    const { active } = this.props
    const cx = classNames('question-a-container', { active })

    return (
      <section className={cx}></section>
    )
  }

}

そして、単純にactiveプロパティを渡して、同名のクラスを切り替えます。

7
Balthazar

これと同様の問題があり、タブ付きドロワーを作成し、タブバーで要求されたコンポーネントのみをレンダリングしていましたが、タブバーで別のコンポーネントに切り替えるたびに子コンポーネントの状態を失うため、このようなソリューションは理想的ではありませんでした。

レンダーでこれを回避するために、すべての子をレンダリングする関数を呼び出します。この関数では、各子をdivでラップし、必要なclassNameをdivに割り当てます。この場合、1回に1つのコンポーネントを表示する必要があるため、クリックしたボタンに応じて、display:blockまたはnoneを使用していました。

renderMyChilds() {
        var renderedComponents = this.props.children.map(function(child, _key) {
            if(this.state.drawerOpened == true) {
                var ElementToRender = child;
                var MyElement = (this.state.buttonSelected != _key) ? <div className="hiddenComponent"><ElementToRender /></div> : <div><ElementToRender /></div>;

            } else {
                var ElementToRender = child;
                var MyElement = <div><ElementToRender className="hiddenComponent" /></div>;
            }
            return (MyElement);
        }, this);
        return renderedComponents;
    }

.hiddenComponent {display:none; }

.visibleComponent {display:block; }

0
CarCarlo