首页 > 解决方案 > React.js 父组件和兄弟组件:一次只保持一个导航栏 div 展开

问题描述

我是 React.js 的新手,我正在通过将用 vanilla javascript 制作的网站转换为 React.js 应用程序来学习。但是,我无法复制导航栏上的下拉菜单功能。

每个部分都由带有 onClick 的 div 组成,该 onClick 可以通过更改overflowheightcss 设置垂直扩展其内容以进行查看。但是,有一个问题:一次只能展开一个下拉菜单。我可以在常规的 JS 站点中做到这一点,但我想在 React 中使用组件之间的父子关系和兄弟关系来在新版本中获得相同的效果。

使用 onClick 事件,应该打开栏的一个选项卡,如果展开任何其他选项卡,请关闭它们。

我尝试使用该问题的解决方案中的信息,但它不是类组件形式,而是处理输入而不是 div。此外,大多数关于这个的搜索结果只关注 vanilla js,而不是 React.js。

起初我试图将状态保存在父组件中,但它们的异步特性使得在正确的时间更新它们变得很困难。

在这里,我剥离了该站点的其他不相关组件,并提供了我的问题的复制。

index.js:

import React from 'react';
import ReactDOM from 'react-dom';
import Tab from './Tab';
import './index.css';

class TabBar extends React.Component {
  constructor() {
    super();
    this.state = {
      //currently no states
    }
  }

  render() {
    return (
      <div className="tab_bar">
        <Tab name="Tab 1" />
        <Tab name="Tab 2" />
        <Tab name="Tab 3" />
      </div>
    )
  }
}

class Main extends React.Component {
  render() {
    return (
      <TabBar />
    )
  }
}
ReactDOM.render(<Main />, document.getElementById('root'));

Tab.js:

import React from 'react';

class TabHeader extends React.Component {
  constructor() {
    super();

  }
  render() {
    return (
      <div className="tab_header" onClick={this.props.onClick}>
        <h3>{this.props.name}</h3>
      </div>
    )
  }
}

class Tab extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      height: "7vh",
      overflow: "hidden"
    }
    this.changeTabHeight = this.changeTabHeight.bind(this);
  }

  changeTabHeight() {
    if (this.state.height === "7vh") {
      this.setState({
        height: "20vh",
        overflow: "unset"
      });
    } else {
      this.setState({
        height: "7vh",
        overflow: "hidden"
      });
    }
  }

  render() {
    return (
      <div className="tab_container" style={{overflow: this.state.overflow, height: this.state.height}}>
        <TabHeader name={this.props.name} onClick={this.changeTabHeight} />
        <div className="tab">
          <p>This is the tab content.</p>
        </div>
      </div>
    )
  }
}

export default Tab;

索引.css:

.body, .root {
  width: 100%;
  height: 100%;
}

.tab_bar {
  width: 100%;
  height: 10%;
  display: flex;
}

.tab_container{
  height: 7vh;
  margin: 0;
  display: inline-block;
  background-color: #82d86c;
  border-left: 1px solid #99ff7f;
  -webkit-transition: height 0.5s;
  transition: height 0.5s;
  overflow: hidden;
}

.tab_header {
  display: flex;
  align-items: center;
  vertical-align: middle;
  height: 7vh;
  text-align: center;
  background-color: #99ff7f;
}

.tab_header h3 {
  margin: 0;
}

.tab {
  margin: 0;
  padding: 5px;
}

预期结果:单击导航栏上的选项卡时,它会展开并在其下方显示信息。再次单击它会将选项卡恢复到其原始高度并隐藏。在当前打开的选项卡中单击另一个选项卡将关闭旧选项卡并展开新选项卡。

实际结果:目前它们按预期扩展,但它们还没有相互关闭的方法。它需要父级中的结构来跟踪下拉列表。

标签: javascriptreactjs

解决方案


您当前遇到的问题是您直接在Tab组件中处理状态,该组件是内部的子组件TabBar

如您所料,低于模块化方式。


index.js:我们可以通过创建一个 Tab 数组并遍历该选项卡来改进它

import React from "react";
import ReactDOM from "react-dom";
import Tab from "./Tab";
import "./index.css";

class TabBar extends React.Component {
  constructor() {
    super();
    this.state = {
      selectedTab: null,
    };
    this.onSelectTab = this.onSelectTab.bind(this);
  }

  onSelectTab(tabIndex) {
    // In case the user clicks again on the tab, unselect it
    const selectedTab = this.state.selectedTab === tabIndex ? false : tabIndex;
    this.setState({ selectedTab });
  }

  render() {
    return (
      <div className="tab_bar">
        <Tab
          name="Tab 1"
          isSelected={this.state.selectedTab === 0}
          onClickTab={() => this.onSelectTab(0)}
        />
        <Tab
          name="Tab 2"
          isSelected={this.state.selectedTab === 1}
          onClickTab={() => this.onSelectTab(1)}
        />
        <Tab
          name="Tab 3"
          isSelected={this.state.selectedTab === 2}
          onClickTab={() => this.onSelectTab(2)}
        />
      </div>
    );
  }
}

class Main extends React.Component {
  render() {
    return <TabBar />;
  }
}
ReactDOM.render(<Main />, document.getElementById("root"));

Tab.js:请记住,最好使用没有状态的功能组件。

import React from "react";

class TabHeader extends React.Component {
  constructor() {
    super();
  }
  render() {
    return (
      <div className="tab_header" onClick={this.props.onClickTab}>
        <h3>{this.props.name}</h3>
      </div>
    );
  }
}

class Tab extends React.Component {
  constructor(props) {
    super(props);
  }

  render() {
    // Moved the style in the CSS rather than in function + state
    // The !!this.props.isSelected allow to always return false / true response
    return (
      <div className={ `tab_container is-selected-${!!this.props.isSelected}`}>
        <TabHeader name={this.props.name} onClickTab={this.props.onClickTab} />
        <div className="tab">
          <p>This is the tab content.</p>
        </div>
      </div>
    );
  }
}

export default Tab;

index.css:添加这两个类,因为最好在 css 中使用样式而不是直接在组件内部

.is-selected-true {
  height: "20vh",
  overflow: "unset"
}


.is-selected-false {
  height: "7vh",
  overflow: "hidden"
}

推荐阅读