0

Hello I have a component as follows I'm using usestate hook to initialize the myFeeds variabele to the feeds array and I'm running an effect to add any data from server (socket.io) However when I'm console logging the result myFeeds is giving me an empty array what could be the possible reasons?

const Dashboard = ({feeds}) => {

useEffect(() => {
getFeeds(); // this fetches feeds array
}, []);

const [myFeeds, setMyFeeds] = useState(feeds);

// useEffect hook to get real time messages and add it to feed

useEffect(() => {
let mount = true;

io.on('created' (data) => {
  if(mount) {
  data && setMyFeeds([...feeds, data]);
  }
 }
}, [])


console.log(feeds); // gives array of objects
console.log(myFeeds); // giving empty array

return(
<FeedBody body={myFeeds} />
);

}

// mapstatetoprops here

getFeeds is an action creator which is as follows

export const getFeeds = () =>  async dispatch => {

const res = await axios.get('/feeds');
// dispatch type get feeds here set payload to result of above operation
}
  • Please update your question with a [mcve] demonstrating the problem, ideally a **runnable** one using Stack Snippets (the `[<>]` toolbar button). Stack Snippets support React, including JSX; [here's how to do one](http://meta.stackoverflow.com/questions/338537/). You'll have to use `setTimeout` to simulate the `io.on()` stuff, but otherwise it should be possible to replicate this in a Stack Snippet. – T.J. Crowder May 05 '20 at 13:36
  • 1
    Where and how you are updating the feeds received from `getFeeds`? – Milind Agrawal May 05 '20 at 13:37
  • can you show the ```getFeeds``` function? – Hadi Pawar May 05 '20 at 13:38
  • 1
    looks like `feeds` is being received by the parent component as a result of an async operation, I think you need to have another `useEffect` hook to monitor change in `props.feeds` and set the state there. `if(feeds) setMyFeeds(feeds)` and the initial state of myFeeds can be an empty array. – Rohit Kashyap May 05 '20 at 13:51

1 Answers1

1

I don't know if it's the problem, but the way you're taking the feeds prop and turning it into the myFeeds state is definitely problematic, in a couple of ways:

  1. Your component won't see updates to its feeds prop if the parent calls it again with a new value. Remember that the value you pass into useState is only used when your component is first created, it's not used on subsequent calls to your component function (the current state of your component is used instead).

  2. The feeds you're using in your io.on callback may be stale, because you have an empty dependencies array in your useEffect call; by the time io.on calls your callback, your component function may have been called with an updated feeds (which you would see in your console.log) but the one used with setMyData will be stale (the original one closed over by the callback when it was created during the first call to your component function).

Here's what I'm referring to in #2:

useEffect(() => {
    let mount = true;

    io.on('created' (data) => {
        if(mount) {
            data && setMyFeeds([...feeds, data]);
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^−−−−−−−−−−−− may be stale!
        }
    }
}, [])

You probably want to move the io.on call out of this component and put it in the parent, and have the parent re-render this component when feeds changes.

If you decide not to do that, the minimum change you need to make is to not use that stale feeds parameter value. Instead, use the callback form:

useEffect(() => {
    let mount = true;

    io.on('created' (data) => {
        if(mount) {
            data && setMyFeeds(myFeeds => [...myFeeds, data]);
// −−−−−−−−−−−−−−−−−−−−−−−−−−−−^^^^^^^^−−−−−−−^^^^^^^−−−−−−−−−−−− up to date
        }
    }
}, [])

But again, your component will miss an updates to feeds that the arent sends it.

This note in the documentation about copying props to state when using hooks may be a useful read.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Sir I have tried this approach to update ```myFeeds``` in real time whenever some one posts a feed ```myFeeds``` should be populated with feeds posted up untill that point as well as latest feed I think I'm missing a crucial point as ```getFeeds``` is a restful call which is unaware of realtime call so ```feeds``` prop will be stale. Should i use event based approach which runs ```getFeeds``` when certain type of event occurs? – Yashwanth somayajula May 05 '20 at 14:45
  • 1
    I don't know your setup, but on the face of it the event you're using is fine, the issue I see with the code is having feeds come to the component in two ways. That's why I think that handler should be in the parent, rather than in this component -- or, of course, this component should get the initial set of feeds itself and not have a `feeds` prop. – T.J. Crowder May 05 '20 at 14:51
  • I'm using redux mapDispatchToProps to get ```getFeeds``` action and calling it in ```useEffect``` hook should shift code to `````` component and then update the state in ``` – Yashwanth somayajula May 05 '20 at 14:58
  • 1
    @Yashwanthsomayajula - Basically what I'm saying is that the initial set of feeds and the update should be handled within the same component, rather than having the `Dashboard` receive the initial set of feeds as a prop but updates via its own event handler. Whether you do that in `Dashboard`'s parent, `Dashboard`, or `FeedBody` is up to you, and you know the overall context far better than I. :-) – T.J. Crowder May 05 '20 at 15:34