添加对 useEffect() 的依赖会导致无限循环。但是删除依赖会导致组件在数据库更新时不重新渲染

时间:2021-05-24 16:06:01

标签: reactjs react-hooks use-effect

以下代码导致 useEffect() 被无限调用。为什么会这样?从 useEffect 依赖项数组中移除 drawboarditems 解决了无限循环,但结果是当用户向数据库添加项目时,DrawingBoard 不会自动重新渲染。

DrawingBoard.jsx

export default function DrawingBoard() {
  const [drawingboarditems, setdrawingboarditems] = useState([]);
  const currentUser = useContext(CurrentUserContext);
  const [loading, setLoading] = useState(true);
  const classes = useStyles();

  useEffect(() => {
    if (currentUser) {
      const items = [];
      //retrieving data from database 
      db.collection("drawingboarditems")
        .where("userID", "==", currentUser.id)
        .get()
        .then((query) => {
          query.forEach((doc) => {
            items.push({
              id: doc.id,
              ...doc.data(),
            });
          });
          setdrawingboarditems(items);
          setLoading(false);
        });
    }
  }, [currentUser, drawingboarditems]);

 return (
    <>
      {loading == false ? (
        <Container>
          <Masonry
            breakpointCols={breakpoints}
            className="my-masonry-grid"
            columnClassName="my-masonry-grid_column"
          >
            {drawingboarditems.map((item) => (
              <div>
                <Note item={item} />
              </div>
            ))}
            <Note form />
          </Masonry>
        </Container>
      ) : (
        <div className={classes.root}>
          <CircularProgress />
        </div>
      )}
    </>
  );

Note.jsx

import React from "react";
import { Card, CardHeader, IconButton, makeStyles } from "@material-ui/core";
import MoreVertIcon from "@material-ui/icons/MoreVert";
import Form from "./Form";
import CardWindow from "./CardWindow"; 

const useStyles = makeStyles((theme) => ({
  card: {
    backgroundColor: theme.palette.secondary.main,
    margin: theme.spacing(1, 0),
  },
}));

export default function Note({ item, form }) {
  const classes = useStyles();
  return (
    <Card className={classes.card}>
      {form ? (
        <Form />
      ) : (
        <CardHeader
          action={
            <CardWindow/>
          }
          title={item.title}
        />
      )}
    </Card>
  );
}

Form.jsx

import React, { useContext } from "react";
import TextField from "@material-ui/core/TextField";
import { makeStyles } from "@material-ui/core/styles";
import AddCircleOutlineIcon from "@material-ui/icons/AddCircleOutline";
import IconButton from "@material-ui/core/IconButton";
import { db } from "../FireStore";
import { CurrentUserContext } from "../utils/Context";

const useStyles = makeStyles((theme) => ({
  form: {
    "& .MuiTextField-root": {
      margin: theme.spacing(1),
      width: "70%", // 70% of card in drawing board
    },
  },
}));

export default function Form() {
  const classes = useStyles();
  const [value, setValue] = React.useState("");
  const currentUser = useContext(CurrentUserContext);

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    if (value) {
      event.preventDefault();
      db.collection("drawingboarditems").add({
        title: value,
        userID: currentUser.id,
      });
      setValue("");
    }
  };

  return (
    <form className={classes.form} noValidate autoComplete="off">
      <div>
        <TextField
          id="standard-textarea"
          placeholder="Add item"
          multiline
          onChange={handleChange}
          value={value}
        />
        <IconButton aria-label="add" onClick={handleSubmit}>
          <AddCircleOutlineIcon fontSize="large" />
        </IconButton>
      </div>
    </form>
  );
}

1 个答案:

答案 0 :(得分:1)

在您的情况下,我会将逻辑移至 DrawingBoard 组件,并将道具传递给子组件,因此当子组件添加项目时,主要组件会知道刷新项目列表。

>

示例(未测试):

将与 FireBase 一起使用的逻辑提取到函数中。这样一来,它们的可重用性就会更高,并且不会给您的代码添加混乱。

const drawingboarditemsCollection = 'drawingboarditems';

function getAllNotes(userID) {
  return db.collection(drawingboarditemsCollection)
    .where("userID", "==", userID)
    .get()
    .then((query) => {
      return query.map(doc => {
        items.push({
          id: doc.id,
          ...doc.data(),
        });
      });
    });
}

function addNote(userID, title) {
  return db.collection(drawingboarditemsCollection).add({
    title,
    userID,
  });
}

DrawingBoard 组件应该处理与服务器的连接,并且应该将函数作为道具传递给孩子:

export default function DrawingBoard() {
  const [drawingboarditems, setdrawingboarditems] = useState([]);
  const currentUser = useContext(CurrentUserContext);
  const [loading, setLoading] = useState(true);
  const classes = useStyles();
  
  // add the logic to get notes by 
  const getNotes = useCallback(() => {
    setLoading(true);
    
    getAllNotes(currentUser.id)
      .then(items => {
        setdrawingboarditems(items);
      })
      .finally(=> {
        setLoading(false);
      });
  }, [currentUser]);
  
  // create the submit handler
  const handleSubmit = value => {
    addNote(currentUser.id, value)
      .then(getNotes); // after adding a note update the items
  }

  // initial get notes, or when currentUser changes
  useEffect(() => {
    getNotes();
  }, [getNotes]);

 return (
    <>
      {loading == false ? (
        <Container>
          <Masonry
            breakpointCols={breakpoints}
            className="my-masonry-grid"
            columnClassName="my-masonry-grid_column"
          >
            {drawingboarditems.map((item) => (
              <div>
                <Note item={item} />
              </div>
            ))}
            <Note form onSubmit={handleSubmit} />
          </Masonry>
        </Container>
      ) : (
        <div className={classes.root}>
          <CircularProgress />
        </div>
      )}
    </>
  );
}

将 onSubmit 函数传递给 Form:

export default function Note({ item, form, onSubmit }) {
  const classes = useStyles();
  return (
    <Card className={classes.card}>
      {form ? (
        <Form onSubmit={onSubmit} />
      ) : (
        <CardHeader
          action={
            <CardWindow/>
          }
          title={item.title}
        />
      )}
    </Card>
  );
}

使用 onSubmit 处理提交:

export default function Form({ onSubmit }) {
  const classes = useStyles();
  const [value, setValue] = React.useState("");

  const handleChange = (event) => {
    setValue(event.target.value);
  };

  const handleSubmit = (event) => {
    if (value) {
      event.preventDefault();
      
      onSubmit(value); // let the parent handle the actual update

      setValue("");
    }
  };

  return ( ... );
}