【问题标题】:react expand and collapse just one panel反应展开和折叠只有一个面板
【发布时间】:2018-06-09 08:40:15
【问题描述】:

在反应方面需要帮助... 尝试实现带有天气信息的可折叠卡片列表。 已经实现了展开和折叠的行为,但是当我点击一个面板时,另一个面板同时打开(我有 2 个面板,需要 7 个来显示一周 7 天的天气)。

如何只打开和关闭一个面板?

代码:

import React, { Component } from 'react';
import Moment from 'react-moment';

import RandomGif from './RandomGif.js';

const urlForCity = city => `https://cors-anywhere.herokuapp.com/http://api.openweathermap.org/data/2.5/forecast/daily?q=${city}&units=metric&cnt=7&appid=1fba7c3eaa869008374898c6a606fe3e`

class OpenWapi extends Component {
  constructor(props) {
    super(props);
    this.state = {
      requestFailed: false,
      shown: false
    }
    this.componentDidMount = this.componentDidMount.bind(this);
    this.toggle = this.toggle.bind(this);
  }

  componentDidMount() {
    fetch(urlForCity(this.props.city))
      .then(response => {
        if(!response.ok) {
          throw Error("Network request failed")
        }
        return response;
      })
      .then(data => data.json())
      .then(data => {
        this.setState({
          weatherData: data
        })
      }, () => {
        this.setState({
          requestFailed: true
        })
      })
  }

  toggle() {
        this.setState({
            shown: !this.state.shown
        });
    }

  render() {

    if(this.state.requestFailed) return <p>Request Failed.</p>;
    if(!this.state.weatherData) return <p>Loading...</p>;

    return (
      <div>
        <p>City: {this.state.weatherData.city.name}</p>

        {/* Day 1 */}
        <div onClick={this.toggle} className="dayWeekItem">
          <div className="top-content">
            <div className="icon-weather"></div>
            <div className="date">
              <div className="weekday">Today</div>
              <div className="day-long"><Moment unix format="MMM DD YYYY">{this.state.weatherData.list[0].dt}</Moment></div>
            </div>
            <div className="temperature">
              <div className="temp-high">{parseInt(this.state.weatherData.list[0].temp.max)}º</div>
              <div className="temp-low">{parseInt(this.state.weatherData.list[0].temp.min)}º</div>
            </div>
          </div>
          <div className={this.state.shown ? "toggleContent-open" : "toggleContent-closed"} >
            <div className="weather-gif" >
              <RandomGif keyword={this.state.weatherData.list[0].weather[0].description} />
            </div>
          </div>
        </div>



        {/* Day 2 */}
        <div onClick={this.toggle} className="dayWeekItem">
          <div className="top-content">
            <div className="icon-weather"></div>
            <div className="date">
              <div className="weekday">Tomorrow</div>
              <div className="day-long"><Moment unix format="MMM DD YYYY">{this.state.weatherData.list[1].dt}</Moment></div>
            </div>
            <div className="temperature">
              <div className="temp-high">{parseInt(this.state.weatherData.list[1].temp.max)}º</div>
              <div className="temp-low">{parseInt(this.state.weatherData.list[1].temp.min)}º</div>
            </div>
          </div>
          <div className={this.state.shown ? "toggleContent-open" : "toggleContent-closed"} >
            <div className="weather-gif" >
              <RandomGif keyword={this.state.weatherData.list[1].weather[0].description} />
            </div>
          </div>
        </div>

        {/* Day 3 */}


        {/* Day 4 */}


        {/* Day 5 */}


      </div>
    )
  }
}

export default OpenWapi;

【问题讨论】:

    标签: javascript reactjs collapse expand


    【解决方案1】:

    我会有一个对象来表示状态,每个面板都有一个字段。

    像这样:

    constructor(props) {
        ...
        this.state = {
          requestFailed: false,
          shown: {}
        }
        ...
    }
    
    ...
    
    toggle(panelNumber) {
       this.setState({
            shown: {
                ...this.state.shown,
                [panelNumber]: !this.state.shown[panelNumber]
            }
        });
    }
    
    ...
    

    toogle 函数是这样使用的,比如第 1 天:

    <div onClick={() => this.toggle(1)} className="dayWeekItem">
        ...
    </div>
    

    并以 html 显示,例如,第 1 天:

          <div className={this.state.shown[1] ? "toggleContent-open" : "toggleContent-closed"} >
            <div className="weather-gif" >
              <RandomGif keyword={this.state.weatherData.list[0].weather[0].description} />
            </div>
          </div>
    

    【讨论】:

    • 感谢@FedericoSawady,您的解决方案非常简单并且效果很好。
    • @Bruno 欢迎您。请不要忘记给你投票最佳答案。
    • 完成@Federico ;)
    【解决方案2】:

    它们都会随着你的实现而崩溃。

    你有一个状态

    state = {
      shown: true
    }
    

    你有一个功能来切换它

    toggle = () => {
       this.setState(shown: !this.state.shown)
    }
    

    您渲染组件,在两个地方使用this.state.shown,但值将始终为一个truefalse

    render() {
        return(<div .....//something>
            <div onClick={this.toggle}>
               { this.state.shown ? <SomeComponent or HTML Tag> : null }
            </div>
            <div onClick={this.toggle}>
              { this.state.shown ? <SomeComponent or HTML Tag> : null }
            </div>
        </div>)
    }
    

    所以无论你在哪里切换,一旦状态更新并再次调用render 方法来绘制视图,divs get the sameBoolean` 的两个部分的值。因此,它们都崩溃了。

    我能为这个问题提供的最佳解决方案是:

    创建一个单独的组件,它有两个工作要做: 1.保持自己的状态,崩溃truefalse。 2. 渲染给它的孩子,不要想知道他们可能是什么。

    那么说

     class WeatherWidget extends React.PureComponent {
       state= {
         shown: true
       }     
       toggle = () => this.setState({shown: !this.state.shown})
       render() {
           return(
             <div onClick={this.toggle} className="dayWeekItem">
                <div className="top-content">
                <div className="icon-weather"></div>
                <div className="date">
                  <div className="weekday">Today</div>
                  <div className="day-long">
                        <Moment unix format="MMM DD YYYY">{this.props.date}</Moment>
                  </div>
                </div>
                <div className="temperature">
                  <div className="temp-high">{parseInt(this.props.maxTemp)}º
                  </div>
                  <div className="temp-low">{parseInt(this.props.minTemp)}º
                  </div>
                </div>
             </div>
                 <div className={this.state.shown ? "toggleContent-open" : "toggleContent-closed"} >
                     <div className="weather-gif" >
                        <RandomGif keyword={this.props.gifDescription} />
                     </div>
                  </div>
         </div>
           )
       }
    
    
     }
    

    所以你创建了一个可重用的组件来管理自己的状态(React Paradigm/Composition 带来可重用性)

    至于显示多个小部件

    class OpenWapi extends Component {
       constructor(props) {
          super(props);
          this.state = {
            requestFailed: false,
            shown: false
          }
          this.componentDidMount = this.componentDidMount.bind(this);
          this.toggle = this.toggle.bind(this);
        }
    
        componentDidMount() {
          fetch(urlForCity(this.props.city))
          .then(response => {
          if(!response.ok) {
             throw Error("Network request failed")
          }
          return response;
         })
         .then(data => data.json())
         .then(data => {
         this.setState({
            weatherData: data
           })
         }, () => {
           this.setState({
             requestFailed: true
         })
      })
    }
    render() {
        if(this.state.requestFailed) return <p>Request Failed.</p>;
        if(!this.state.weatherData) return <p>Loading...</p>;
    
        return(
        <div>
           <p>City: {this.state.weatherData.city.name}</p>
           <WeatherWidget 
              date={this.state.weatherData.list[0].dt}
              maxTemp={this.state.weatherData.list[0].temp.max}
              minTemp={this.state.weatherData.list[0].temp.min}
              gifDescription=
                    {this.state.weatherData.list[0].weather[1].description}
    
           />
           <WeatherWidget 
              date={this.state.weatherData.list[1].dt}
              maxTemp={this.state.weatherData.list[1].temp.max}
              minTemp={this.state.weatherData.list[1].temp.min}
              gifDescription=
                    {this.state.weatherData.list[1].weather[1].description}
    
           />
        </div>
        )
    } 
    

    希望这可以解决用例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-30
      • 2016-02-28
      • 2016-04-17
      • 1970-01-01
      • 1970-01-01
      • 2022-07-22
      • 2014-11-25
      • 2016-06-20
      相关资源
      最近更新 更多