无法将初始数据设置为 child 组件,因为它在页面加载时未定义

Can not set initial data to child component because it is undefined on page load

我有 5 个城市,它们的天气在页面加载时显示在卡片上,当您单击一个城市时,我希望更多详细信息显示在另一张卡片上,但详细卡片需要从第一个城市开始的初始数据卡,但我似乎无法让它工作。我猜问题是数据在页面加载时未定义,所以我不确定如何显示它?我的猜测是我必须在定义数据时有条件地加载详细卡片,但我不确定如何编写它。

Parent

const fetchCity = async (city) => {
    const res = await axios.get(`https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${key}`);

    return {
        description: res.data.weather[0].description,
        icon: res.data.weather[0].icon,
        temp: res.data.main.temp,
        city: res.data.name,
        country: res.data.sys.country,
        id: res.data.id,
    };
};

function App() {
    const [data, setData] = useState([]);

    const [activeWeather, setActiveWeather] = useState([]);

    useEffect(() => {
        const fetchCities = async () => {
            const citiesData = await Promise.all(["Ottawa", "Toronto", "Vancouver", "California", "London"].map(fetchCity)).catch((err) => {
                console.log("error:", err);
            });

            setData((prevState) => prevState.concat(citiesData));
        };

        fetchCities();
    }, []);

    const handleClick = (event) => {
        const weather = JSON.parse(event.target.dataset.value);
        setActiveWeather(weather);
    };

    return (
        <div className="App">
            <Header />
            <Container>
                <Row>
                    <Col>
                        <WeatherPanel data={data} handleClick={handleClick} />
                    </Col>
                    <Col>
                        //Probably where I have conditionally render?
                        <ActiveWeather activeWeather={activeWeather} data={data[0]} />
                    </Col>
                </Row>
            </Container>
        </div>
    );
}

export default App;

卡片详情

import React, { useEffect, useState } from "react";
import { Container, Card } from "react-bootstrap";

const ActiveWeather = (props) => {
    const [weather, setWeather] = useState(props.data);

    console.log("props", props);

    useEffect(() => {
        setWeather(props.activeWeather);
    }, [props.activeWeather]);

    console.log(weather);
    return (
        <Container>
            <Card>
                <Card.Header> {weather?.city}</Card.Header>
                {weather?.temp}
            </Card>
        </Container>
    );
};

export default ActiveWeather;

初始卡

const WeatherPanel = (props) => {
    return (
        <div>
            <Container fluid>
                <Card style={{ boxShadow: "0  0  10px 2px lightgrey" }}>
                    <Card.Header> Favorite Location</Card.Header>
                    <ListGroup variant="flush">
                        <ListGroup.Item>
                            {props.data.map((item) => (
                                <ListGroup.Item key={item.id} data-value={JSON.stringify(item)} onClick={props.handleClick}>
                                    <img src={`http://openweathermap.org/img/wn/${item.icon}@2x.png`} alt="Weather Icon" />
                                    {item.city + ", " + item.country}
                                </ListGroup.Item>
                            ))}
                        </ListGroup.Item>
                    </ListGroup>
                </Card>
            </Container>
        </div>
    );
};

export default WeatherPanel;

正如您所说,第一次渲染时的数据是未定义的。您可以像这样有条件地在父级呈现 Detail Component

function App() {
  const [data, setData] = useState([]);

  const [activeWeather, setActiveWeather] = useState([]);

  useEffect(() => {
    const fetchCities = async () => {
      const citiesData = await Promise.all(
        ['Ottawa', 'Toronto', 'Vancouver', 'California', 'London'].map(
          fetchCity
        )
      ).catch(err => {
        console.log('error:', err);
      });

      setData(prevState => prevState.concat(citiesData));
    };

    fetchCities();
  }, []);

  const handleClick = event => {
    const weather = JSON.parse(event.target.dataset.value);
    setActiveWeather(weather);
  };

  return (
    <div className="App">
      <Header />
      <Container>
        <Row>
          <Col>
            <WeatherPanel data={data} handleClick={handleClick} />
          </Col>
          <Col>
            // Since you want to prevent the undefined to passed to children
            // You need conditionally render OR conditionally passed the data

            // #1 conditionally render
            // check the length of the weather
            {activeWeather.length && 
            <ActiveWeather activeWeather={activeWeather} data={data[0]} />}

            // #2 conditionally pass the data
            <ActiveWeather activeWeather={activeWeather || []} data={data[0] || []} />
          </Col>
        </Row>
      </Container>
    </div>
  );
}

export default App;

Detail Card 中,您的代码会像这样更好

import React, { useEffect, useState } from "react";
import { Container, Card } from "react-bootstrap";

const ActiveWeather = (props) => {
    return (
        <Container>
            <Card>
                <Card.Header> {props?.weather?.city}</Card.Header>
                {props?.weather?.temp}
            </Card>
        </Container>
    );
};

export default ActiveWeather;