React:从 MUI 更新单选按钮时,Formik 不会触发渲染

问题描述 投票:0回答:1

我正在使用 Formik 和 MUI 开发 ToDo 应用程序。我从弹出框内的选择按钮切换为单选按钮,单击图标时会打开该弹出框。但是使用单选按钮,更改其中的值时不会触发渲染。我尝试了许多其他方法来处理它并强制更新渲染。没有任何效果,所以我将当前混乱的代码状态放入沙箱中,并希望任何人都可以帮助我。

https://codesandbox.io/p/sandbox/kfng7y

它应该更新状态并重新渲染组件以显示正确的状态,但它不会重新渲染并且不显示更新后的状态

reactjs material-ui formik
1个回答
0
投票

好吧,我调试了几天才学到一些东西-

将 JSX 置于状态会创建“状态闭包”。他们将“记住”创建它们时存在的值,而不是渲染它们时存在的值。

TL;博士 任何需要对变化的值保持反应的组件都需要直接在 JSX 中呈现,而不是存储在状态中。

  1. 将数据存储在状态中,而不是组件中。

  2. 将状态用于不可变对象(字符串、数字、布尔值)或数据对象或 UI 状态标志(isOpen、activeTab 等)

  3. 不要对 JSX 元素、React 组件以及任何其他需要访问 props 或其他变化值的东西使用状态。

所以不要将 JSX 置于这样的状态:

const TodoForm: React.FC<ToDoFormProps> = ({ onSubmit }) => {
  const [popoverContent, setPopoverContent] = useState<React.ReactNode>(null);

// DON'T: Storing JSX in state that needs access to changing values
const handleIconClick = (
    event: React.MouseEvent<HTMLElement>,
    content: React.ReactNode
  ) => {
    setAnchorEl(event.currentTarget);
    setPopoverContent(content);
  };

您应该仅将 JSX 保留在返回语句中。切勿将其置于某种状态:

const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null);
  const [activePopover, setActivePopover] = useState<"date" | "tag" | "priority" | null>(null);

// DO: Render components directly with current values
  const handleIconClick = (
    event: React.MouseEvent<HTMLElement>,
    type: "date" | "tag" | "priority"
  ) => {
    setAnchorEl(event.currentTarget);
    setActivePopover(type);
  };

const handlePopoverClose = () => {
    setAnchorEl(null);
    setActivePopover(null);
  };


  // Render popover content based on active type
  const renderPopoverContent = (values: any, setFieldValue: any) => {
    switch (activePopover) {
      case "date":
        return (
          <LocalizationProvider dateAdapter={AdapterDateFns}>
            <DesktopDateTimePicker
              ampm={false}
              label="Fälligkeitsdatum"
              value={values.dueDate}
              onChange={(newValue) => {
                setFieldValue("dueDate", newValue);
              }}
              minDate={new Date()}
            />
          </LocalizationProvider>
        );
      case "tag":
        return (
          <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
            <RadioGroup
              name="tag"
              value={values.tag}
              onChange={(e) => {
                setFieldValue("tag", e.target.value);
              }}
            >
              {tags.map((tag) => (
                <FormControlLabel
                  key={tag}
                  value={tag}
                  control={<Radio />}
                  label={tag}
                  sx={{
                    border: "1px solid #ccc",
                    borderRadius: 2,
                    p: 1,
                    m: 0.5,
                  }}
                />
              ))}
            </RadioGroup>
          </Box>
        );
      case "priority":
        return (
          <Box sx={{ display: "flex", flexDirection: "column", gap: 1 }}>
            <RadioGroup
              name="priority"
              value={values.priority}
              onChange={(e) => {
                setFieldValue("priority", e.target.value);
              }}
            >
              <FormControlLabel
                value="low"
                control={<Radio />}
                label="Niedrig"
                sx={{
                  border: "1px solid #ccc",
                  borderRadius: 2,
                  p: 1,
                  m: 0.5,
                }}
              />
              <FormControlLabel
                value="medium"
                control={<Radio />}
                label="Mittel"
                sx={{
                  border: "1px solid #ccc",
                  borderRadius: 2,
                  p: 1,
                  m: 0.5,
                }}
              />
              <FormControlLabel
                value="high"
                control={<Radio />}
                label="Hoch"
                sx={{
                  border: "1px solid #ccc",
                  borderRadius: 2,
                  p: 1,
                  m: 0.5,
                }}
              />
            </RadioGroup>
          </Box>
        );
      default:
        return null;
    }
  };

return (
           <>
           <Grid container spacing={1} alignItems="center">
              <IconButton onClick={(e) => handleIconClick(e, "date")}>
                <CalendarTodayIcon />
              </IconButton>
              <IconButton onClick={(e) => handleIconClick(e, "tag")}>
                <TagIcon />
              </IconButton>
              <IconButton onClick={(e) => handleIconClick(e, "priority")}>
                <PriorityHighIcon />
              </IconButton>
            </Grid>

            <Popover
              open={Boolean(anchorEl)}
              anchorEl={anchorEl}
              onClose={handlePopoverClose}
              anchorOrigin={{
                vertical: "bottom",
                horizontal: "center",
              }}
              transformOrigin={{
                vertical: "top",
                horizontal: "center",
              }}
            >
              <Box sx={{ p: 2 }}>
                {renderPopoverContent(values, setFieldValue)}
              </Box>
            </Popover>
            </>


)

© www.soinside.com 2019 - 2024. All rights reserved.