【问题标题】:React useEffect to obtain Autocomplete options (material UI) from Cloud FirestoreReact useEffect 从 Cloud Firestore 获取 Autocomplete 选项(材质 UI)
【发布时间】:2020-11-09 12:05:57
【问题描述】:

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

我的 Firestore 中有一个名为“组织”的集合。该文档有一个名为“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 控制台中有值时,控制台将 shortName 记录为未定义)。

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

【问题讨论】:

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


    【解决方案1】:
    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...
    

    【讨论】:

    • hmmm - 没有错误 - 但选项也不会呈现。我有一系列使用 Material UI Typography 触发的警告:validateDOMNesting(...):
      不能作为

      的后代出现。在进一步研究之前,我将尝试解决这些问题并再次测试。感谢您到目前为止的帮助。

    • 我已经删除了所有排版警告,并且仍在生成一个没有任何选项的表单(控制台中有 2 个)。
    • 我在上面添加了下一次尝试 - 以及错误消息
    • 我还尝试创建一个新的第三个组织来测试更新是否会强制表单使用选项 - 但它不会
    • 我明白了......如答案中所述,useEffect 中不需要依赖orgList。只要organisations 集合中的数据发生变化,它就会注册新的回调
    【解决方案2】:

    我最终找到了有效的方法。似乎存在限制从 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 属性可用于自动完成。我不知道如何让它工作 - 但现在,这是另一天的问题。

    【讨论】:

      猜你喜欢
      相关资源
      最近更新 更多
      热门标签