0

My ArticleList component is successfully getting & displaying the user's list of articles from firestore when I first load the app. The user can click a "Remove Article" button, which successfully removes the article from the subcollection in firestore, but it causes an error in the rendering of the react component, which seems to still be trying to render the article that was just removed and is now null. Is there something else I can do to make my react component continuously listen to the firestore data? If possible, I'd like to keep this a functional component and use hooks rather than making it a class, but I'm still learning how to use react hooks and therefore struggling a bit.

ArticleList component:

const ArticleList = (props) => {
const firestore = useFirestore();
const userId = props.auth.uid;
useFirestoreConnect([
{
  collection: 'users',
  doc: userId,
  subcollections: [{collection: 'articles'}],
  storeAs: userId + '::articles'
}
]);
const myArticles = useSelector(state => state.firestore.data[`${userId}::articles`]);
const dispatch = useDispatch();
const removeArticle = useCallback(
articleId => dispatch(removeArticleFromFirebase({ firestore }, articleId)),
[firestore]
);
if (props.auth.uid) {
return(
  <div>
    <h3>My Articles</h3>
    <p>Currently signed in: {props.auth.email}</p>
    <br/>
    {myArticles ? (
        Object.keys(myArticles).map(articleId => {
          let article = myArticles[articleId];
          let articleInformation = '';
          if (articleId === props.currentPaperId) {
            articleInformation =
              <div>
                <p>{article.year}</p>   
                <p>{article.description}</p>
                <a target="_blank" href={article.downloadUrl}><button className='waves-effect waves-light btn-small'>See article</button></a>
                <button className='waves-effect waves-light btn-small' onClick={() => {removeArticle(articleId);}}>Remove from My Articles</button>
              </div>;
          }
          let authorName = '';
          if (article.author) {
            authorName = ` by ${article.author}`;
          }
          if (article) {
            return <span key={articleId}>
              <li onClick={() => {dispatch(selectArticle(articleId));}}>
                <em>{article.title}</em>{authorName}
              </li>{articleInformation}
            </span>;
          } else {
            return null;
          }
        })
      ) : (
        <h4>No articles yet</h4>
      )
    }
  </div>
);
} else {
  return null;
}
};
const mapStateToProps = (state) => {
  return {
    currentPaperId: state.currentPaperId,
    auth: state.firebase.auth
  };
};
export default compose(connect(mapStateToProps))(ArticleList);

And the removeArticleFromFirebase action:

export const removeArticleFromFirebase = ({ firestore }, id) => {
return (dispatch, getState) => {
const userId = getState().firebase.auth.uid;
firestore
  .collection('users')
  .doc(userId)
  .collection('articles')
  .doc(id)
  .delete()
  .then(() => {
    console.log('Deleted article from firestore: ', id);
    dispatch({ type: 'REMOVE_ARTICLE', id });
  })
  .catch(err => {
    console.log('Error: ', err);
  });
};
}

I've tried adding useState and useEffect in the ArticleList as follows (and tried having the component's return statement map through myArticlesState instead of myArticles), but no success:

const [myArticlesState, setMyArticlesState] = useState(myArticles);
useEffect(() => {
setMyArticlesState(myArticles);
}, [myArticles]);

Note: I do not currently have this article list in overall app state/redux store/props at all. This is something I was thinking of trying next, but I decided to post my question first in case I can just use hooks in this component. No other components/parts of the app need access to this particular list.

Console errors: error image 1 error image 2 Github repo: https://github.com/jpremmel/yarp2.0

JPR
  • 3
  • 2
  • Can you show us all of the code involved in this? Please show the entire ArticleList component.. you are clearly attempting to use the property `year` somewhere - looks like line 60 or 66 in your ArticleList component.. – Matt Oestreich Mar 09 '20 at 01:18
  • Added the rest of the component (minus the css styling objects, for clarity). `

    {article.year}

    ` is where the error is thrown. Note that in order for the article's "Remove Article" button to appear, the user must click on it to select it first, hence the `articleInformation` only showing up for the currently selected article.
    – JPR Mar 09 '20 at 01:43
  • ...the `'REMOVE_ARTICLE'` action then clears out the `currentPaperId` in state, since that article has already been removed from firestore. – JPR Mar 09 '20 at 01:46

2 Answers2

1

It's kind of difficult to see what's going on but it appears as though you are trying to use a property on an object that does not exist. Therefore, checking for those properties should help resolve this.

Can you try the follow code as your ArticleList?

const ArticleList = (props) => {
  const firestore = useFirestore();
  const userId = props.auth.uid;

  useFirestoreConnect([{
    collection: 'users',
    doc: userId,
    subcollections: [{ collection: 'articles' }],
    storeAs: userId + '::articles'
  }]);

  const myArticles = useSelector(state => state.firestore.data[`${userId}::articles`]);
  const dispatch = useDispatch();
  const removeArticle = useCallback(articleId => dispatch(removeArticleFromFirebase({ firestore }, articleId)), [firestore]);

  if (props.auth.uid) {
    return (
      <div>
        <h3>My Articles</h3>
        <p>Currently signed in: {props.auth.email}</p>
        <br />
        {myArticles ? (
          Object.keys(myArticles).map(articleId => {
            let article = myArticles[articleId];
            let articleInformation = '';

            if (article) {
              if (
                articleId === props.currentPaperId &&
                article.hasOwnProperty('year') &&
                article.hasOwnProperty('description') &&
                article.hasOwnProperty('downloadUrl')
              ) {
                articleInformation =
                  <div>
                    <p>{article.year}</p>
                    <p>{article.description}</p>
                    <a target="_blank" href={article.downloadUrl}><button className='waves-effect waves-light btn-small'>See article</button></a>
                    <button className='waves-effect waves-light btn-small' onClick={() => { removeArticle(articleId); }}>Remove from My Articles</button>
                  </div>;
              }

              let authorName = '';
              if (article.hasOwnProperty('author') && article.author) {
                authorName = ` by ${article.author}`;
              }

              if (article.hasOwnProperty('title') && article.title) {
                return <span key={articleId}>
                  <li onClick={() => { dispatch(selectArticle(articleId)); }}>
                    <em>{article.title}</em>{authorName}
                  </li>{articleInformation}
                </span>;
              } else {
                return null;
              }
            }
          })
        ) : (
            <h4>No articles yet</h4>
          )
        }
      </div>
    );
  } else {
    return null;
  }
};
const mapStateToProps = (state) => {
  return {
    currentPaperId: state.currentPaperId,
    auth: state.firebase.auth
  };
};
export default compose(connect(mapStateToProps))(ArticleList);
Matt Oestreich
  • 8,219
  • 3
  • 16
  • 41
  • Ah, yes, this helped me get around the issue. Wrapping everything in the `if (article)` block was basically the key, since the issue was happening when `article` was null. Thank you! – JPR Mar 09 '20 at 05:56
0

Can you show us the error? I think it's about the state not being an array after you delete your data just initialize your state with an empty array like this :

Const= [articlesdata,setArticlesData]=useState([]) 

And leave the useEffect as it is

Baha
  • 19
  • 6
  • Sure - I added links to screenshots of the errors to the post – JPR Mar 09 '20 at 01:11
  • Thanks for providing the error, can you show the code of mapping articles – Baha Mar 09 '20 at 01:15
  • Added the rest of the component (minus the css styling objects, for clarity). `

    {article.year}

    ` is where the error is thrown. Note that in order for the article's "Remove Article" button to appear, the user must click on it to select it first, hence the `articleInformation` only showing up for the currently selected article. Then the `'REMOVE_ARTICLE'` action clears out the `currentPaperId` in state, since that article has already been removed from firestore
    – JPR Mar 09 '20 at 01:47