传递给子组件时道具未定义(反应钩子)

Posted

技术标签:

【中文标题】传递给子组件时道具未定义(反应钩子)【英文标题】:Props is undefined when passed to a child component (react hooks) 【发布时间】:2021-11-18 19:03:43 【问题描述】:

我正在构建一个简单的议程组件,但遇到了问题。这个想法是,当一个人点击这一天,然后看到这一天的培训。我的逻辑如下

    点击按钮时,我将状态设置为日期 ID 在现有活动项上三元运算符呈现组件 我将函数调用作为道具传递,它返回最新的对象。

我尝试将函数调用放到 handleClick 函数中,但没有帮助。对我来说,似乎函数没有及时返回值让组件传递它,但我不知道如何绕过这个问题。 这是包含所有内容的代码框 - 请帮助

https://codesandbox.io/s/cranky-johnson-s2dj3?file=/src/scheduledTrainingCard.js

这是父组件的代码,问题就在这里

import React,  useState  from "react";
import  Transition, Grid, Button, Container  from "semantic-ui-react";
import ScheduledTrainingCard from "./scheduledTrainingCard";

function ScheduleComponent(props) 
  const days = [
    
      id: "1",
      dayTrainings: [
        
          time: "20:15:00",
          training: "zumba",
          trainer: "joe"
        ,
        
          time: "16:00:00",
          training: "stretching",
          trainer: "lily"
        
      ],
      date:
        "Thu Dec 28 1995 00:00:00 GMT+0100 (Central European Standard Time)",
      createdAt: "2021-07-14T19:30:59.177Z"
    ,
    
      id: "2",
      dayTrainings: [
        
          time: "23:15:00",
          training: "boxing",
          trainer: "phoebe"
        ,
        
          time: "15:00:00",
          training: "dancing",
          trainer: "kate"
        
      ],
      date: "Thu Sep 23 2021 20:57:38 GMT+0200 (Central European Summer Time)",
      createdAt: "2021-09-23T19:01:53.801Z"
    ,
    
      id: "3",
      dayTrainings: [
        
          time: "23:15:00",
          training: "ballet",
          trainer: "nataly"
        ,
        
          time: "12:00:00",
          training: "crossfit",
          trainer: "sheldon"
        
      ],
      date: "Fri Jul 23 2021 21:02:37 GMT+0200 (Central European Summer Time)",
      createdAt: "2021-09-23T19:03:31.161Z"
    
  ];

  const [activeItem, setActiveItem] = useState("");

  const handleItemClick = (e) => 
    setActiveItem(e.target.name);
  ;

  function showSelectedDay(arr, dayId) 
    arr.forEach((element, index) => 
      if (dayId === element.id) 
        console.log("selected day is " + element.date);
        let trainings = element.dayTrainings;
        trainings.map((training) => 
          return training;
        );
       else 
        console.log("not selected, day id is " + element.id);
      
    );
  

  const ScheduleComponent = (
      <Container style= textAlign: "left" >
        <h1 style= textAlign: "center" >Training Schedule</h1>
        <p>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Incidunt
          dolor unde repudiandae culpa ullam, asperiores officiis ratione
          repellat quaerat nihil vel corporis distinctio vero doloribus dolore
          optio commodi voluptatum inventore.
        </p>

        <Container>
          <Grid style= margin: "2rem auto", textAlign: "center"  relaxed>
            <Button onClick=(e) => handleItemClick(e) name="1">
              Monday
            </Button>
            <Button onClick=(e) => handleItemClick(e) name="2">
              Tuesday
            </Button>
            <Button onClick=(e) => handleItemClick(e) name="3">
              Wednesday
            </Button>
          </Grid>
        </Container>
        <Container>
          activeItem && (
            <ScheduledTrainingCard
              dayTraining=showSelectedDay(days, activeItem)
            ></ScheduledTrainingCard>
          )
        </Container>
      </Container>
   
  );
  return ScheduleComponent;


export default ScheduleComponent;

【问题讨论】:

你 showSelectedDay 函数没有返回任何东西......你从 forEach 循环返回 【参考方案1】:

首先为selectedDays定义一个新状态:

  const [activeItem, setActiveItem] = useState("");
  const [selectedDays, setSelectedDays] = useState([]);

然后根据用户的选择设置setSelectedDays的值和activeItem

  useEffect(() => 
    let filteredDays = days.filter((x) => x.id === activeItem);
    setSelectedDays(filteredDays);
  , [activeItem]);

最后定义一个新函数来渲染这些日子。因为days是一个层次数组,所以需要循环两次:

  const showDays = () => 
    return selectedDays.map((days, index) => 
      return days.dayTrainings.map((item, i) => 
        let dayTraining = 
          id: days.id,
          time: item.time,
          trainer: item.trainer,
          training: item.training
        ;
        return (
          <ScheduledTrainingCard
            dayTrainings=dayTraining
          ></ScheduledTrainingCard>
        );
      );
    );
  ;

const ScheduleComponent = (
    <Transition.Group>
      <Container style= textAlign: "left" >
        <h1 style= textAlign: "center" >Training Schedule</h1>
        <p>
          Lorem ipsum dolor sit amet consectetur adipisicing elit. Incidunt
          dolor unde repudiandae culpa ullam, asperiores officiis ratione
          repellat quaerat nihil vel corporis distinctio vero doloribus dolore
          optio commodi voluptatum inventore.
        </p>

        <Container>
          <Grid style= margin: "2rem auto", textAlign: "center"  relaxed>
            <Button onClick=(e) => handleItemClick(e) name=1>
              Monday
            </Button>
            <Button onClick=(e) => handleItemClick(e) name=2>
              Tuesday
            </Button>
            <Button onClick=(e) => handleItemClick(e) name=3>
              Wednesday
            </Button>
          </Grid>
        </Container>
        <Container>activeItem && selectedDays && showDays()</Container>
      </Container>
    </Transition.Group>
  );
  return ScheduleComponent;

【讨论】:

感谢您对它的改进,使用 useEffect 很不错,我真的很喜欢。我知道如果我们将状态作为一个数组,我可以访问它的内容直到状态改变,对吗? @BadgerWannaBe 绝对是的!当状态发生变化时,您可以通过整个组件中的新状态访问它,而无需将其值作为参数发送给组件内的另一个函数。 @BadgerWannaBe 如果回答成功,您可以接受它以获取更多资源。 嗨 Majid,我正在测试你的答案我遇到了另一个问题 - 我的实际应用程序正在使用 Apolli useQuery hook 来获取数据,在我看来 useEffect 没有看到或得到“days”数组及时。这里看起来完全是const [activeItem, setActiveItem] = useState(""); const [selectedDays, setSelectedDays] = useState([]); const loading, data: getDays: days = = useQuery(FETCH_DAYS_QUERY); useEffect(() =&gt; let filteredDays = days.filter((x) =&gt; x.id === activeItem); console.log(filteredDays); setSelectedDays(filteredDays); , [activeItem]); 我之前没试过useQuery!但是如果你的意思是调用一个api,你只需要在页面加载成功时调用新的api,useEffect(( )=&gt; ,[ ])并获取天数,最后定义一个新的状态并使用api结果设置状态。所以你可以使用新状态而不是静态 arr。【参考方案2】:

问题是您在地图内使用返回。 看看我是怎么做到的:

1- 我们不需要函数showSelectedDay,所以我将其删除。

2- 我们不需要状态const [activeItem, setActiveItem] = useState("");

3- 添加新状态const [dayTrainings, setDayTrainings] = useState([]);

4- 将handleItemClick 更新为:

  const [dayTrainings, setDayTrainings] = useState([]);
  const handleItemClick = (e) => 
    days.forEach((element, index) => 
      if (e.target.name === element.id) 
        console.log("selected day is " + element.date);
        setDayTrainings(element.dayTrainings);
       else 
        console.log("not selected, day id is " + element.id);
      
    );
  ;

5- 在渲染返回中:

      dayTrainings.length > 0 && (
        <ScheduledTrainingCard
          dayTraining=dayTrainings
        ></ScheduledTrainingCard>
      )

ScheduledTrainingCard 的示例:

export default function scheduledTrainingCard(props) 
  console.log(props.dayTraining);
  return (<>
      
        props.dayTraining.map((item, index) => 
        <p key=index>
          item['time']<br/>
          item['training']<br/>
          item['trainer']<br/>
          <br/><br/>
        </p>)
      
    </>);

输出示例: output

【讨论】:

感谢您在将数组保存到状态时的彻底解释和改进。我对渲染有疑问 - 所以在这个解决方案中,当我们有超过 0 次培训时,它会被渲染,我找不到任何缺陷,但仍然没有很多人使用它(在教程和其他示例中)。真的不明白为什么不。【参考方案3】:

你可以这样做

const handleItemClick = (e) => 
    days.map(item => 
      if(item.id === e.target.name)
        setActiveItem(item)
      
    )
  ;

并改变这一行

activeItem && 
          activeItem.dayTrainings.map(day => <ScheduledTrainingCard dayTraining=day/>)
          

【讨论】:

以上是关于传递给子组件时道具未定义(反应钩子)的主要内容,如果未能解决你的问题,请参考以下文章

反应:作为道具的数组显示为未定义

将两个道具从父组件传递给子组件会导致父组件未定义

将状态传递给子组件时未定义状态

在功能性反应组件中获取未定义的道具

反应:子组件未接收通过“this.state”传递的道具

使用 Context API 在函数组件中反应未定义的状态属性