反应useEffect以从Cloud Firestore获取自动完成选项(材料UI)

时间:2020-07-20 09:01:39

标签: google-cloud-firestore material-ui react-hooks

我正在尝试使用react useEffect钩子从firestore中获取数据并将其提供给Material UI自动完成选择菜单上的options属性。

我在消防处有一个叫做“组织”的收藏。该文档具有名为“ shortName”的属性。

我正在尝试从集合中获取数据,然后使用它在名为orgList的属性上设置状态,然后可以在选择菜单中的中使用它。

这就是我正在尝试的。

import React, { useState, useEffect } from 'react';
import Checkbox from '@material-ui/core/Checkbox';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '@material-ui/icons/CheckBox';
import firebase from "../../../../../firebase";

const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;

export default function CheckboxesTags() {
  const [orgList, setOrgList] = useState();
  const [selectedOrgList, setSelectedOrgList] = useState();

  useEffect(() => {
    firebase
      .firestore()
      .collection("organisations")
      .onSnapshot(snapshot => {
        const orgList = snapshot.docs.map(doc => ({
          id: doc.id,
          ...doc.data(),
        }))
        setOrgList(orgList)
      })
  }, [orgList])
   
  

  return (
      <div>
      
        <Autocomplete
        multiple
        id="checkboxes-tags-demo"
        options={orgList}
        disableCloseOnSelect
        getOptionLabel={(option) => option.shortName}
        renderOption={(option, { selected }) => (
            <React.Fragment>
            <Checkbox
                icon={icon}
                checkedIcon={checkedIcon}
                style={{ marginRight: 8 }}
                checked={selected}
            />
            {option.shortName}
            </React.Fragment>
        )}
        style={{ width: 500 }}
        renderInput={(params) => (
            <TextField {...params} 
            variant="outlined" 
            label="Select Organisation" 
            placeholder="Acme Inc" 
          />
        )}
        />
    </div>
  );
}

我收到的错误消息是:

TypeError:无法读取未定义的属性'shortName'

下一个尝试

使用下面来自gdh的建议,这是下一次尝试。

import React, { useState, useEffect } from 'react';
import Checkbox from '@material-ui/core/Checkbox';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '@material-ui/icons/CheckBox';
import firebase from "../../../../../firebase";

const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;

export default function CheckboxesTags() {
  const [orgList, setOrgList] = useState([]);
  const [selectedOrgList, setSelectedOrgList] = useState();
  const [loading, setLoading ] = useState(true);
  const [ error, setError ] = useState(false);

  useEffect(() => {
    const unsubscribe = firebase
      .firestore()
      .collection("organisations")
      .onSnapshot((snapshot) => {
        const orgList = snapshot.docs.map((doc) => ({
          id: doc.id,
          shortName: doc.shortName
        }));
console.log(orgList)

        setOrgList(orgList);
      }, () => {
          setError(true)
        });
        setLoading(false);
        return() => unsubscribe();
    
  }, [orgList]);

  useEffect(() => {
    firebase
      .firestore()
      .collection("organisations")
      .get()
      .then((snapshot) => {
        const orgList = snapshot.docs.map((doc) => ({
          id: doc.id,
          shortName: doc.shortName
        }));
        setOrgList(orgList);
      });
  }, []);

   

  return (
      <div>
      
        <Autocomplete
        multiple
        id="checkboxes-tags-demo"
        options={orgList}
        disableCloseOnSelect
        getOptionLabel={(orgList) => orgList.shortName}
        renderOption={(orgList, { selected }) => (
            <React.Fragment>
            <Checkbox
                icon={icon}
                checkedIcon={checkedIcon}
                style={{ marginRight: 8 }}
                checked={selected}
            />
            {orgList.shortName} 
            
            </React.Fragment>
        )}
        style={{ width: 500 }}
        renderInput={(params) => (
            <TextField {...params} 
            variant="outlined" 
            label="Select Organisation" 
            placeholder="Acme Inc." 
          />
        )}
        />
    </div>
  );
}

控制台日志显示两个orgList id,但是shortName是未定义的。

我看到一条错误消息:

TypeError:无法读取未定义的属性“ toLowerCase”。

为解决此错误,我添加了:

    ignoreCase = {false}

到“自动完成”标题标签,但仍然存在相同的错误。控制台会记录一条错误消息:

警告:React无法识别DOM上的ignoreCase道具 元件。如果您有意让它作为自定义项出现在DOM中 属性,则将其拼写为小写ignorecase。如果你 意外地从父组件传递了它,将其从DOM中删除 元素

我尝试将Firestore中的shortName重命名为'short',以查看是否可以避免区分大小写的问题,但是仍然存在相同的错误(控制台在Firestore控制台中具有值时,其简短记录为undefined)。

我知道表单是从firestore中读取的,因为当我尝试将选项设置为文档的id时,表单会加载,并且id将打印为值。

2 个答案:

答案 0 :(得分:0)

  1. 您没有orgList的初始值。提供一个空白数组。
  2. 您仅注册onSnapshot回调,该回调仅在organisations集合更改时执行,但您未在装入时获取任何数据。这意味着仅当有人在organisation集合中进行更改时,您的自动填充功能才会具有值。因此,请保持另一个useEffect并获取数据。

重构代码

export default function CheckboxesTags() {
  const [orgList, setOrgList] = useState([]); 
  const [selectedOrgList, setSelectedOrgList] = useState();

  useEffect(() => {
    firebase
      .firestore()
      .collection("organisations")
      .onSnapshot((snapshot) => {
        const orgList = snapshot.docs.map((doc) => ({
          id: doc.id,
          ...doc.data(),
        }));
        setOrgList(orgList);
      });
  }, []);

  useEffect(() => {
    firebase
      .firestore()
      .collection("organisations")
      .get()
      .then((snapshot) => {
        const orgList = snapshot.docs.map((doc) => ({
          id: doc.id,
          ...doc.data(),
        }));
        setOrgList(orgList);
      });
  }, []);
//.... rest of code...

答案 1 :(得分:0)

我找到了行之有效的方法-最终。似乎存在限制从Firebase文档读取单个属性的问题。在我的快照中,我试图读取:doc.shortName / doc.short。它必须是:

 shortName: doc.data().shortName,

我最终并不需要gdh提议的第二个.get useEffect。

这对我有用:

import React, { useState, useEffect } from 'react';
import Checkbox from '@material-ui/core/Checkbox';
import TextField from '@material-ui/core/TextField';
import Autocomplete from '@material-ui/lab/Autocomplete';
import CheckBoxOutlineBlankIcon from '@material-ui/icons/CheckBoxOutlineBlank';
import CheckBoxIcon from '@material-ui/icons/CheckBox';
import firebase from "../../../../../firebase";

const icon = <CheckBoxOutlineBlankIcon fontSize="small" />;
const checkedIcon = <CheckBoxIcon fontSize="small" />;

export default function CheckboxesTags() {
  const [orgList, setOrgList] = useState([]);
  const [selectedOrgList, setSelectedOrgList] = useState();
  const [loading, setLoading ] = useState(true);
  const [ error, setError ] = useState(false);

  useEffect(() => {
    // if (doc.exists) {
      const unsubscribe = firebase
        .firestore()
        .collection("organisations")
        .onSnapshot((snapshot) => {
          const orgList = snapshot.docs.map((doc) => ({
            id: doc.id,
            shortName: doc.data().shortName

          }));
          console.log(orgList)
          setOrgList(orgList);
        }, () => {
          setError(true)
        });
        setLoading(false);
        return() => unsubscribe();
     
    
  }, [orgList]);

  // useEffect(() => {
  //   firebase
  //     .firestore()
  //     .collection("organisations")
  //     .get()
  //     .then((snapshot) => {
  //       const orgList = snapshot.docs.map((doc) => ({
  //         id: doc.id,
  //         ...doc.data()
  //       }));
  //       setOrgList(orgList);
  //     });
  // }, []);

   

  return (
      <div>
      
        <Autocomplete
        multiple
        id="checkboxes-tags-demo"
        options={orgList}
        disableCloseOnSelect
        getOptionLabel={(option) => option.shortName}
        renderOption={(orgList, { selected }) => (
            <React.Fragment>
            <Checkbox
                icon={icon}
                checkedIcon={checkedIcon}
                style={{ marginRight: 8 }}
                checked={selected}
            />
            {orgList.shortName}  
            </React.Fragment>
        )}
        style={{ width: 500 }}
        renderInput={(params) => (
            <TextField {...params} 
            variant="outlined" 
            label="Select Organisation" 
            placeholder="Acme Inc." 
          />
        )}
        />
    </div>
  );
}

对于toLowerCase错误-有一个filterOptions属性可用于自动完成功能。我不知道该如何工作-但就目前而言,这又是一个问题。