简体   繁体   中英

Why is my state replacing my posts when I add a new one using React hooks and React context

I'm creating a new app where I want to be able to post updates to my friends. A micro-blogging site.

I want to learn how to update the app using React hooks and React's context API. I created the following provider that takes the state as the value... I want to be able to add a new post and then update the state's posts so that I don't have to fetch the database again (using firestore) I'm really trying to save myself a call to the db...

Basically, when I call createNewPost within the state, I want to be able to update the current posts section of the state: state.posts but when I update the state after the API call is successful, my entire posts array gets replaced for some reason. Not sure what I might be doing wrong...

import { createContext, useState } from 'react';
import { createDoc, getWhere } from '../utils/database/db';

export const PostDataContext = createContext();

const SetDataContextProvider = ({ children }) => {
    const [state, setState] = useState({
        posts: [],
        timelinePosts: [],
        createNewPost: async (collection, payload) => {
            const doc = await createDoc(collection, payload)
            payload.id = doc?.doc?.id;
            updateStatePosts(payload);
            return doc;
        },
        getPostsByUserId: async (userId) => {
            const dataReceived = await getWhere('/posts', userId, 'userId')
            setState({ ...state, posts: dataReceived })
        }
    });
    const updateStatePosts = (payload) => {
        console.log('why is state posts empty?!', state);
        setState({ ...state, posts: [payload, ...state.posts] })
    }
    return <PostDataContext.Provider value={state}>
        {children}
    </PostDataContext.Provider>
}

export default SetDataContextProvider;

If I had to guess I would say you have a stale enclosure of your initial empty posts state within the updateStatePosts function used in your state. You can use a functional state update to access the previous state to update from. Functional state updates allow you to update from the previous state, not the state the update was enqueued/enclosed in.

const SetDataContextProvider = ({ children }) => {
    const [state, setState] = useState({
        posts: [],
        timelinePosts: [],
        createNewPost: async (collection, payload) => {
            const doc = await createDoc(collection, payload)
            payload.id = doc?.doc?.id;
            updateStatePosts(payload);
            return doc;
        },
        getPostsByUserId: async (userId) => {
            const dataReceived = await getWhere('/posts', userId, 'userId')
            setState(prevState => ({
                ...prevState, // <-- preserve any previous state
                posts: dataReceived
            }))
        }
    });

    const updateStatePosts = (payload) => {
        setState(prevState => ({ // <-- previous state to this update
            ...prevState,
            posts: [payload, ...prevState.posts],
         }));
    };

    return <PostDataContext.Provider value={state}>
        {children}
    </PostDataContext.Provider>
}

This function is wrong:

const updateStatePosts = (payload) => {
        console.log('why is state posts empty?!', state);
        setState({ ...state, posts: [payload, ...state.posts] })
    }

You're using the spread operator correctly for your state, but posts is directly replaced. You might want to use the following setState :

setState({ ...state, posts: [...payload,...state.posts] })

Despite that, you should also refactor your state . Functions are not states so put them outside your state.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM