除非我先刷新,否则 React 应用程序中的 Firestore 文档字段更新会给出 TypeError
Posted
技术标签:
【中文标题】除非我先刷新,否则 React 应用程序中的 Firestore 文档字段更新会给出 TypeError【英文标题】:Firestore doc field update in react app giving TypeError unless I refresh first 【发布时间】:2022-01-17 00:01:57 【问题描述】:我有一个 react 应用程序,它允许为登录用户(杂货、待办事项等)创建列表并将数据存储在 firebase.firestore 上。它仍在使用 localhost 进行开发。如果我创建一个新列表,向其中添加项目并立即编辑项目描述,我会收到 TypeError 并且项目描述不会在应用程序或 Firestore 中更新。如果我选择要显示的不同列表,然后在编辑项目描述之前单击返回新列表或刷新浏览器,一切正常。如果我专门创建新列表、向其中添加项目并立即尝试编辑项目描述,它只是不起作用,即使我在提交更改之前检查了 firestore 并且新列表和新项目显示存在。
知道为什么除非我先在浏览器中刷新应用程序,否则 handleSubmit 中的 await checkDoc 不起作用吗?
我在应用刷新前后添加了 Firestore 屏幕截图,以防万一。他们看起来和我一模一样。
Github repo 是分支编辑项 github repo
当我更新项目描述而不刷新时,控制台中显示错误:
TypeError: u.indexOf is not a function
at Function.e.ot (prebuilt-67479dbf-318e5a2c.js:878)
at Bs (prebuilt-67479dbf-318e5a2c.js:14560)
at e.doc (prebuilt-67479dbf-318e5a2c.js:18281)
at handleSubmit (EditItem.js:32)
at htmlUnknownElement.callCallback (react-dom.development.js:3945)
at Object.invokeGuardedCallbackDev (react-dom.development.js:3994)
at invokeGuardedCallback (react-dom.development.js:4056)
at invokeGuardedCallbackAndCatchFirstError (react-dom.development.js:4070)
at executeDispatch (react-dom.development.js:8243)
at processDispatchQueueItemsInOrder (react-dom.development.js:8275)
at processDispatchQueue (react-dom.development.js:8288)
at dispatchEventsForPlugins (react-dom.development.js:8299)
at react-dom.development.js:8508
at batchedEventUpdates$1 (react-dom.development.js:22396)
at batchedEventUpdates (react-dom.development.js:3745)
at dispatchEventForPluginEventSystem (react-dom.development.js:8507)
at attemptToDispatchEvent (react-dom.development.js:6005)
at dispatchEvent (react-dom.development.js:5924)
at unstable_runWithPriority (scheduler.development.js:646)
at runWithPriority$1 (react-dom.development.js:11276)
at discreteUpdates$1 (react-dom.development.js:22413)
at discreteUpdates (react-dom.development.js:3756)
at dispatchDiscreteEvent (react-dom.development.js:5889)
除非我先刷新,否则带有 handleSubmit 函数的 EditItem 组件会出错:
import React, useState from 'react';
import db from '../../hooks/useAuth';
import
Button,
Input,
Center,
FormControl,
Flex,
Heading,
from '@chakra-ui/react';
const EditItem = (
user,
items,
setItems,
setEditItem,
currentList,
editItem,
themeObj,
) =>
const checkDoc = db.collection('users').doc(user.uid);
const [newDesc, setNewDesc] = useState('');
const handleSubmit = async e =>
e.preventDefault();
try
console.log(editItem);
console.log(newDesc);
console.log(currentList);
console.log(editItem.id);
await checkDoc
.collection(currentList)
.doc(editItem.id)
.update( desc: newDesc );
const editedList = items.map(item =>
item.id === editItem.id ? ...item, desc: newDesc : item
);
setItems(editedList);
console.log(editedList);
setNewDesc(`$editItem.desc`);
setEditItem(null);
catch (err)
console.log(err);
finally
;
return (
<Flex w="100%" grow="1" direction="column" p=6>
<Center
mb="1rem"
borderRadius="lg"
p=3
bg=themeObj.bg
color=themeObj.color
>
<Heading size="md">Edit Item Description</Heading>
</Center>
<Center
mb="1rem"
borderRadius="lg"
p=3
bg=themeObj.bgItem
color=themeObj.colorItem
>
newDesc.length ? newDesc : editItem.desc
</Center>
<form
label="New Item Description"
onSubmit=handleSubmit
style= width: '100%'
>
<FormControl>
<Input
required
variant="outline"
autoFocus
// ref=inputRef
type="text"
id="newDesc"
placeholder="New Item Description"
required
value=newDesc
onChange=e => setNewDesc(e.target.value)
/>
<Button
variant="solid"
mt=4
type="submit"
aria-label="Rename List"
color="white"
_hover=
background: `$themeObj.checkScheme`,
bg="black"
>
Update
</Button>
<Button
variant="solid"
mt=4
type="button"
onClick=() =>
setEditItem(null);
setNewDesc(`$editItem.desc`);
aria-label="cancel"
color="white"
_hover=
background: `$themeObj.checkScheme`,
bg="red"
>
Cancel
</Button>
</FormControl>
</form>
</Flex>
);
;
export default EditItem;
仪表板组件(EditItem 的父级):
import React, useState, useEffect from 'react';
import InputGroup, Stack, Flex from '@chakra-ui/react';
import firebase from 'firebase/app';
import EditItem from '../iList/EditItem';
import Loader from '../iList/Loader';
import AddItem from '../iList/AddItem';
import SearchItem from '../iList/SearchItem';
import Content from '../iList/Content';
import Footer from '../iList/Footer';
import useAuth, db from '../../hooks/useAuth';
import 'firebase/firestore';
const Dashboard = (
setAppTheme,
loaderLoading,
setLoaderLoading,
setIsLoading,
isLoading,
fetchError,
setFetchError,
themeObj,
currentList,
setCurrentList,
) =>
console.log(currentList);
const user = useAuth();
const [items, setItems] = useState([]);
const [search, setSearch] = useState('');
const [editItem, setEditItem] = useState(null);
const [newItem, setNewItem] = useState('');
const checkDoc = db.collection('users').doc(user.uid);
const itemsCollection = db
.collection('users')
.doc(user.uid)
.collection(currentList);
useEffect(() =>
checkIfInitialized();
const getUserPrefs = async () =>
try
const userList = await checkDoc.get();
setCurrentList(userList.data().currentlist);
setAppTheme(userList.data().currenttheme);
setFetchError(null);
catch (err)
setFetchError(err.message);
console.log(err.message);
finally
setLoaderLoading(false);
setIsLoading(false);
;
getUserPrefs();
, []);
useEffect(() =>
const getItems = async () =>
try
const data = await itemsCollection.get();
const listItems = data.docs.map(doc => (
...doc.data(),
id: doc.id,
));
setItems(listItems);
setFetchError(null);
catch (err)
setFetchError(err.message);
finally
setLoaderLoading(false);
;
getItems();
, [currentList]);
const addItem = async item =>
const id = items.length ? Number(items[items.length - 1].id) + 1 : 1;
console.log(id);
const newItemDate = new Date();
const dateStr = `$
newItemDate.getMonth() + 1
/$newItemDate.getDate()/$newItemDate.getFullYear()`;
const myNewItem =
id: id,
checked: false,
desc: item,
date: dateStr,
;
const listItems = [...items, myNewItem];
setItems(listItems);
const addedDoc = db
.collection('users')
.doc(user.uid)
.collection(currentList)
.doc(`$myNewItem.id`);
await addedDoc
.set( desc: myNewItem.desc, checked: false, date: dateStr )
.then(() =>
console.log('Document successfully written!');
)
.catch(error =>
console.error('Error writing document: ', error);
);
;
const handleCheck = async id =>
const listItems = items.map(item =>
item.id === id ? ...item, checked: !item.checked : item
);
setItems(listItems);
const myItem = items.filter(item => item.id === id);
console.log(myItem);
const updatedDoc = db
.collection('users')
.doc(user.uid)
.collection(currentList)
.doc(`$id`);
console.log('here');
await updatedDoc
.update(
checked: !myItem[0].checked,
)
.then(() =>
console.log('Document successfully updated!');
)
.catch(error =>
// The document probably doesn't exist.
console.error('Error updating document: ', error);
);
;
const handleDelete = async id =>
const listItems = items.filter(item => item.id !== id);
setItems(listItems);
const deletedDoc = db
.collection('users')
.doc(user.uid)
.collection(currentList)
.doc(`$id`);
await deletedDoc
.delete()
.then(() =>
console.log('Document successfully deleted!');
)
.catch(error =>
console.error('Error removing document: ', error);
);
;
const handleSubmit = e =>
e.preventDefault();
if (!newItem) return;
addItem(newItem);
setNewItem('');
;
const checkIfInitialized = () =>
const docRef = db.collection('users').doc(user.uid);
docRef
.get()
.then(doc =>
if (doc.exists)
console.log('Document data:', doc.data());
else
// doc.data() will be undefined in this case
console.log('No such document!');
initializeUserDb();
)
.catch(error =>
console.log('Error getting document:', error);
);
;
const initializeUserDb = async () =>
const firstEntry = db.collection('users').doc(user.uid);
await firstEntry
.set(
currentlist: currentList,
mylists: firebase.firestore.FieldValue.arrayUnion('My List'),
currenttheme: 'default',
email: user.email,
)
.then(() =>
console.log('currentlist successfully written!');
)
.catch(error =>
console.error('Error writing document: ', error);
);
;
return (
<>
editItem && (
<EditItem
items=items
setItems=setItems
currentList=currentList
user=user
editItem=editItem
setEditItem=setEditItem
themeObj=themeObj
/>
)
!editItem && (
<>
<Stack mb=3 w="100%" p=3>
<InputGroup>
<AddItem
themeObj=themeObj
newItem=newItem
setNewItem=setNewItem
handleSubmit=handleSubmit
/>
</InputGroup>
<InputGroup>
<SearchItem
themeObj=themeObj
search=search
setSearch=setSearch
/>
</InputGroup>
</Stack>
<Flex
w="100%"
flexDirection="column"
flexGrow="1"
justifyContent="flex-start"
align-items="center"
overflowY="auto"
>
(isLoading || loaderLoading) && <Loader />
fetchError && (
<p style= color: 'red' >`Error: $fetchError`</p>
)
!fetchError && !loaderLoading && !isLoading && (
<Content
setEditItem=setEditItem
themeObj=themeObj
items=items.filter(item =>
item.desc.toLowerCase().includes(search.toLowerCase())
)
handleDelete=handleDelete
handleCheck=handleCheck
/>
)
</Flex>
</>
)
<Footer bg=themeObj.bg color=themeObj.color length=items.length />
</>
);
;
export default Dashboard;
刷新应用程序之前的firestore屏幕截图(我刚刚创建了“测试列表”和两个项目,更新项目描述不起作用)
刷新应用后的firestore屏幕截图(更新项目描述有效)
【问题讨论】:
【参考方案1】:我想通了……
我用来更新文档的新创建项目的 id 是一个数字。但是firestore上的id是字符串,不是数字。
将 handleSubmit 回调从 .doc(editItem.id)
更改为修复它:
.doc(`$editItem.id`)
【讨论】:
以上是关于除非我先刷新,否则 React 应用程序中的 Firestore 文档字段更新会给出 TypeError的主要内容,如果未能解决你的问题,请参考以下文章
除非刷新页面,否则React loadable无法在每个新构建上加载组件
除非我刷新,否则 jQuery mobile 中的 JavaScript 不起作用
SmartEdit:除非刷新页面,否则嵌套 CMS 组件中的更改不会反映
添加“onmouseenter”(悬停)音频效果,但除非我先点击,否则它不会在 chrome 中播放?