javascript 反应展开和折叠只有一个面板
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/48013570/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
react expand and collapse just one panel
提问by Bruno
Need help with react...Trying to implement a collapsible list of cards with weather information. Already implemented the behavior of expand and collapse, but when i clicked on one panel the other panel open at the same time (i have 2 panels and need 7 to display weahter for 7 days of the week).
需要帮助做出反应...试图实现一个带有天气信息的可折叠卡片列表。已经实现了展开和折叠的行为,但是当我单击一个面板时,另一个面板同时打开(我有 2 个面板,需要 7 个面板来显示一周 7 天的天气)。
How can i open and close just one panel?
如何只打开和关闭一个面板?
Code:
代码:
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)}o</div>
<div className="temp-low">{parseInt(this.state.weatherData.list[0].temp.min)}o</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)}o</div>
<div className="temp-low">{parseInt(this.state.weatherData.list[1].temp.min)}o</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;
采纳答案by Federico Sawady
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:
像这样使用工具函数,例如,第 1 天:
<div onClick={() => this.toggle(1)} className="dayWeekItem">
...
</div>
And to show in html, for instance, Day 1:
并以 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>
回答by nirbhaygp
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.shownin two places, but the value will always be one trueor false
你渲染组件,this.state.shown在两个地方使用 ,但值总是一true或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 rendermethod is called again to paint the view, both sections of divs get the sameBoolean` value. Therefore, they both collapse.
所以无论你在哪里切换,一旦状态更新并render再次调用方法来绘制视图,divs get the same布尔值的两个部分。因此,他们都崩溃了。
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 trueor false.
2. Render the children given to it without wondering what they might be.
创建一个单独的组件,它有两个工作要做: 1. 维护自己的状态,折叠true或false。2. 呈现给它的孩子,不要想知道他们可能是什么。
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)}o
</div>
<div className="temp-low">{parseInt(this.props.minTemp)}o
</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)
所以你创建了一个可重用的组件来管理自己的状态(React Paradigm/ Composition 带来了可重用性)
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.
希望这可以解决用例。

