组件未将元素存储到变量中(也许还没有渲染?)

时间:2018-12-31 21:35:48

标签: reactjs

我正在尝试将DOM元素存储在React组件内的变量中。

我在组件的render()函数中声明此变量,如下所示:

render() {
  const aside = document.querySelector('aside');
    ...
    ...
}

鉴于此声明是在return之前声明的,我猜想在声明此声明时,const asidenull,也许这就是为什么它不起作用的原因

我应该在哪里声明此变量?还是有更好的方法在React中存储像这样的DOM元素变量?

另一个问题:这个变量是引用浏览器DOM元素还是React的虚拟DOM元素?

这里是完整的组件:

import React, { Component } from 'react';

class Sidebar extends Component {
  render() {
    const aside = document.querySelector('aside');

    function hideSidebar() {
      aside.classList.remove('show');
    }

    function openSidebar() {
      aside.classList.add('show');
      aside.addEventListener('mouseleave', 'hideSidebar');
    }

    return (
        <aside>
          <svg onClick={openSidebar}
            ...
          </svg>
          <ul>
            <li onClick={chooseSvg}>Dreamcast</li>
            <li onClick={chooseSvg}>Jetson</li>
          </ul>
        </aside>
    );
  }
}

export default Sidebar;

当我单击<svg>元素并触发onClick={openSidebar}时,出现错误:

TypeError: Cannot read property 'classList' of null

2 个答案:

答案 0 :(得分:2)

您的假设是正确的,因为您正在获得null,因为在调用render时DOM尚未更新。当您需要对原始DOM元素的引用时,React提供了Ref的概念来安全地这样做。您可以在Refs and the DOM下的官方React文档中阅读关于Ref的信息以及如何使用它们。

在大多数情况下,与像这样手动操作DOM相比,React为我们提供了更好的选择。对于上述情况,我强烈建议您使用组件状态来保存一个值(例如isSidebarOpen)并根据该状态属性切换className。类似于以下示例:

import React, { Component } from 'react';

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

    this.state = {
      isSidebarOpen: false,
    };

    this.openSidebar = this.openSidebar.bind(this);
    this.hideSidebar = this.hideSidebar.bind(this);
  }

  openSidebar() {
    this.setState({
      isSidebarOpen: true,
    });
  }

  hideSidebar() {
    this.setState({
      isSidebarOpen: false,
    });
  }

  render() {
    let asideClassName = this.state.isSidebarOpen
      ? 'show'
      : 'hide';

    return (
        <aside className={asideClassName} onMouseLeave={this.hideSidebar}>
          <svg onClick={this.openSidebar}
            ...
          </svg>
          <ul>
            <li onClick={chooseSvg}>Dreamcast</li>
            <li onClick={chooseSvg}>Jetson</li>
          </ul>
        </aside>
    );
  }
}

export default Sidebar;

答案 1 :(得分:2)

首先,直接在React组件内部操作dom不是一个好主意。 除了原始答案之外,您还可以使用react挂钩-https://reactjs.org/docs/hooks-intro.html

import React, { useState } from "react";

function Sidebar() {
  const [sideBarOpen, setSideBarOpen] = useState(false);

  const hideSideBar = () => {
    setSideBarOpen(false);
  };

  const showSideBar = () => {
    setSideBarOpen(true);
  };

  const chooseSvg = () => {
    //
  };

  return (
    <aside className={sideBarOpen ? "show" : "hide"} onMouseLeave={hideSideBar}>
      <svg onClick={showSideBar}>
        //...
      </svg>
      <ul>
        <li onClick={chooseSvg}>Dreamcast</li>
        <li onClick={chooseSvg}>Jetson</li>
      </ul>
    </aside>
  );
}

export default Sidebar;