На основе предложения @BrettEast;
Я знаю, что это не то, что вы хотите услышать, но я бы, вероятно, предложил использовать useReducer reactjs .org / docs / hooks-reference.html #usereducer, а не useState для отслеживания массива объектов. Это может упростить отслеживание обновлений. Что касается вашей ошибки, я не думаю, что setDocList, даже с функцией prevState, гарантированно будет актуальным к тому времени, когда вы войдете в этот оператор if.
Я использую useReducer
вместо useState
и вот рабочий код:
import React, {useReducer, useEffect} from 'react'
import { withAuthorization } from '../../Session'
import DocDetailsCard from './Doc';
const initialState = [];
/**
* reducer declaration for useReducer
* @param {[*]} state the current use reducer state
* @param {{payload:*,type:'add'|'modify'|'remove'}} action defines the function to be performed and the data needed to execute such function in order to modify the state variable
*/
const reducer = (state, action) => {
switch (action.type) {
case 'add':
return [action.payload, ...state]
case 'modify':
const modIdx = state.findIndex((doc, idx) => {
if (doc.id === action.payload.id) {
console.log(`modified data found in idx: ${idx}, id: ${doc.id}`);
return true;
}
return false;
})
let newModState = state;
newModState.splice(modIdx,1,action.payload);
return [...newModState]
case 'remove':
const rmIdx = state.findIndex((doc, idx) => {
if (doc.id === action.payload.id) {
console.log(`data removed from idx: ${idx}, id: ${doc.id}, fullData: `,doc);
return true;
}
return false;
})
let newRmState = state;
newRmState.splice(rmIdx,1);
return [...newRmState]
default:
return [...state]
}
}
const DocList = ({firebase}) => {
const [state, dispatch] = useReducer(reducer, initialState)
useEffect(() => {
const unSubListener = firebase.wxDocs()
.orderBy("TimeStamp", "asc")
.onSnapshot({
includeMetadataChanges: true
}, docsSnap => {
docsSnap.docChanges()
.forEach(docSnap => {
let source = docSnap.doc.metadata.fromCache ? 'local cache' : 'server';
if (docSnap.type === 'added') {
dispatch({type:'add', payload:{
source: source,
id: docSnap.doc.id,
...docSnap.doc.data()
}})
}
if (docSnap.type === 'modified') {
dispatch({type:'modify',payload:{
source: source,
id: docSnap.doc.id,
...docSnap.doc.data()
}})
}
if (docSnap.type === 'removed'){
dispatch({type:'remove',payload:{
source: source,
id: docSnap.doc.id,
...docSnap.doc.data()
}})
}
})
})
return () => {
unSubListener();
}
}, [firebase]);
return (
<div >
{
state.map(eachDoc => (
<DocDetailsCard key={eachDoc.id} details={eachDoc} />
))
}
</div>
)
}
const condition = authUser => !!authUser ;
export default React.memo(withAuthorization(condition)(DocList));
также в соответствии с @HMR, используя функцию обратного вызова setState: вот обновленный код, который также работал, если вы используете useState()
.
import React, { useState, useEffect} from 'react'
import { withAuthorization } from '../../Session'
import DocDetailsCard from './Doc';
const DocList = ({firebase}) => {
const [docList, setDocList ] = useState([]);
const classes = useStyles();
useEffect(() => {
const unSubListener = firebase.wxDocs()
.orderBy("TimeStamp", "asc")
.onSnapshot({
includeMetadataChanges: true
}, docsSnap => {
docsSnap.docChanges()
.forEach(docSnap => {
let source = docSnap.doc.metadata.fromCache ? 'local cache' : 'server';
if (docSnap.type === 'added') {
setDocList(current => [{
source: source,
id: docSnap.doc.id,
...docSnap.doc.data()
}, ...current]);
console.log('document added: ', docSnap.doc.data());
}
if (docSnap.type === 'modified') {
setDocList(current => current.map(item => item.id === docSnap.doc.id ? {
source: source,
id: docSnap.doc.id,
...docSnap.doc.data()} : item )
)
}
if (docSnap.type === 'removed'){
setDocList(current => {
const rmIdx = current.findIndex((doc, idx) => {
if (doc.id === docSnap.doc.id) {
return true;
}
return false;
})
let newRmState = current;
newRmState.splice(rmIdx, 1);
return [...newRmState]
})
}
})
})
return () => {
unSubListener();
}
}, [firebase]);
return (
<div >
{
docList.map(eachDoc => (
<DocDetailsCard key={eachDoc.id} details={eachDoc} />
))
}
</div>
)
}
const condition = authUser => !!authUser ;
export default React.memo(withAuthorization(condition)(DocList));
Спасибо, надеюсь, эта помощь поможет тем, у кого возникла подобная проблема.