React 如何从子组件访问父节点的 DOM 元素

7

我有一个如下所示的网格组件:

import React, { Component } from 'react';

import Action from './action.jsx';

class Grid extends Component {

    constructor(props) {
        super(props);
        this.maxN = 110;
        this.tempArray = [];
        this.question;
    }

    getRandomN() {
        var randomN = Math.floor((Math.random() * this.maxN) + 1);

        if(this.tempArray.indexOf(randomN) === -1) {
            this.tempArray.push(randomN);
        }
        else {
            randomN = this.getRandomN();
        }

        return randomN;
    }

    getRandomQuestion() {   
        this.question = this.props.current.data.questions[this.getRandomN()];
        return this.question;
    }

    render() {
        this.getRandomQuestion();

        return (
            <section className="game">
                <div className="grid">

                    <div className="row">
                        <div ref="n1"></div>
                        <div ref="n2"></div>
                        <div ref="n3"></div>
                        <div ref="n4"></div>
                        <div ref="n5"></div>
                        <div ref="n6"></div>
                    </div>

                    <div className="row">
                        <div ref="n7"></div>
                        <div ref="n8"></div>
                        <div ref="n9"></div>
                        <div ref="n10"></div>
                        <div ref="n11"></div>
                        <div ref="n12"></div>
                    </div>

                    <div className="row">
                        <div ref="n13"></div>
                        <div ref="n14"></div>
                        <div ref="n15"></div>
                        <div ref="n16"></div>
                        <div ref="n17"></div>
                        <div ref="n18"></div>
                    </div>

                    <div className="row">
                        <div ref="n19"></div>
                        <div ref="n20"></div>
                        <div ref="n21"></div>
                        <div ref="n22"></div>
                        <div ref="n23"></div>
                        <div ref="n24"></div>
                    </div> 
                </div>

                <Action question={this.question} getRandomQuestion={this.getRandomQuestion.bind(this)}/>
            </section>
        );
    }
}

export default Grid;

在“操作”组件内,根据来自“getNewQuestion”的正确或错误答案,我需要访问网格组件中的一个随机网格元素。(任意随机从“n1”到“n24”,分配给每个ref属性)

import React, { Component } from 'react';

class Action extends Component {
    constructor(props) {
        super(props);

        this.state = {
            question: props.question
        }
    }

    getNewQuestion(e) {
        console.log(this.state.question.correct_option);

        let answerId = "option_" + this.state.question.correct_option;

        if(e.target.getAttribute('data-question') == answerId) {
            this.setState({
                question: this.props.getRandomQuestion()
            });
        }
        else {
            console.log('wrong');

            React.findDOMNode(this.refs.n1).classList.add('fdsdfsdfsdfsdfsfsdf');
        }
    }

    render() {
        let state = this.state;

        return(
            <div className="action">
                <div className="action-question">
                    <h3>{state.question.question}</h3>
                </div>

                <div className="action-answers">
                    <p data-question="option_1" onClick={this.getNewQuestion.bind(this)}>{state.question.option_1}</p>
                    <p data-question="option_2" onClick={this.getNewQuestion.bind(this)}>{state.question.option_2}</p>
                    <p data-question="option_3" onClick={this.getNewQuestion.bind(this)}>{state.question.option_3}</p>
                    <p data-question="option_4" onClick={this.getNewQuestion.bind(this)}>{state.question.option_4}</p>
                </div>
            </div>
        );
    }
}   

export default Action;

在"getNewQuestion"的"if"语句内,我想做如下操作:

n2.classList.addClass('hidden');

我无法从"Action"组件中访问父节点的DOM节点。

3个回答

2

孩子是否真的需要直接访问父级DOM?难道不应该由父组件知道如何呈现自己吗?如果是这样,那么您可以使用回调将其传递给子组件,以便子组件有可能在它应该改变时通知父组件。

const Child = ({modifyParent}) => (
  <div onClick={ modifyParent } >Click me!</div>
);

const Parent = () => {
  const modifyMyOwnStyle = event => {
    // here you have easy access 
    // if you want to style the parent.
    alert('Modifying style, based on child call');
  }

  return (
    <Child modifyParent={ modifyMyOwnStyle }/>
  );
};

ReactDOM.render(<Parent />, document.getElementById('root'));

可运行的JSFiddle演示 在这里


怎样才能访问父级元素中的单个 dome 节点(<div ref="n1"></div>),以便能够添加诸如“hidden”之类的类? - Aessandro
1
你的div可以根据父级状态来确定类名,例如<div className={this.state.someVariable} />。然后你可以在modifyMyOwnStyle函数中修改该状态(例如this.setState({ someVariable: 'hidden' }))。当状态更新时,这将导致重新渲染。然后{ this.state.someVariable }将解析为新值,从而更改赋予div的className。注意,在我的示例中,我使用了功能组件,它们没有状态。因此,您需要将它们转换为有状态的组件。 - jonahe
嗨,我同意你的观点,但是为每个网格元素都设置一个状态(总共有24个)会不会很昂贵(从性能方面考虑)? - Aessandro
它更昂贵。但我认为除非我们谈论数万美元,否则甚至不会引起注意。因此,过于担心这一点可能是过早优化的情况。 - jonahe

1
您可以获取组件的引用并将其传递给其子组件,如下所示:
render() {
  return (
    <div ref={node => this.node = node}>
      <SomeChild parent={this.node} />
    </div>
  )
}

在这里阅读更多相关内容:https://facebook.github.io/react/docs/refs-and-the-dom.html

然而,我必须说这通常不是一个好主意,如果您真的需要传递节点或者有其他解决此问题的方法,建议重新考虑。

编辑:正如jonahe的评论所示,通常可以通过向子组件传递回调函数来解决问题,当父组件需要执行某些操作时,可以触发该回调函数。


1
这似乎不起作用 - 直到渲染完成后,引用才可用。 - Moos

0

比直接访问父节点的DOM更好的方法是,您可以使用回调属性来代替。

例如:

class Grid extends Component {
  constructor(props) {
    super(props)

    this.accessRandomElement = this.accessRandomElement.bind(this)
  }

  accessRandomElement() {
    // do you thing
  }

  render() {
    this.getRandomQuestion()

    return (
      <section className="game">
        ...
        <Action
          question={this.question}
          onAccessYourRandomElement={this.accessRandomElement}
          ///
        />
      </section>
    )
  }
}

然后在 Action 内部调用 this.props.onAccessYourRandomElement()


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