react expand and collapse just one panel

前端 未结 2 633
难免孤独
难免孤独 2021-02-14 18:34

Need help with react... Trying to implement a collapsible list of cards with weather information. Already implemented the behavior of expand and collapse, but w

相关标签:
2条回答
  • 2021-02-14 19:10

    I would have an object to represent the state, a field for each panel.

    Like this:

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

    The toogle function is used like this, for instance, Day 1:

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

    And to show in html, for instance, Day 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>
    
    0 讨论(0)
  • 2021-02-14 19:35

    They all will collapse always with your implementation.

    You have a state

    state = {
      shown: true
    }
    

    You have a function to toggle it

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

    And you render the component, using the this.state.shown in two places, but the value will always be one true or false

    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>)
    }
    

    So where ever you toggle, once the state is updated and render method is called again to paint the view, both sections of divs get the sameBoolean` value. Therefore, they both collapse.

    Best Solution I can offer for this problem will be:

    Create a separate component which has two jobs to be do: 1. Maintains its own state, of collapse true or false. 2. Render the children given to it without wondering what they might be.

    So let say

     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>
           )
       }
    
    
     }
    

    So you create a reusable component which manages its own state ( React Paradigm/ Composition brings reusability)

    As for displaying multiple widgets

    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>
        )
    } 
    

    Hopefully, this solves the use case.

    0 讨论(0)
提交回复
热议问题