我试图弄清楚如何创建清除功能,因为我不断收到错误

时间:2020-03-10 00:20:13

标签: reactjs firebase firebase-realtime-database use-effect

我一直想知道如何创建清除函数,因为我不断收到错误消息,如果我从useEffect依赖项中删除了“注释”,错误就会消失,但是应用程序不会实时更新,这是一个问题。如果有人使用过React和实时数据库甚至是Firestore,并且对我应该做什么有任何想法,请告诉我。

import React, { useContext, useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { toast } from 'react-toastify';

import User from '../assets/images/user.svg';

import { AuthContext } from '../helpers/firebaseAuth';
import firebase from '../helpers/Firebase';
import Loading from '../helpers/Loading';

export const Comments = ({ match, history }) => {
    const { register, handleSubmit, reset } = useForm();

    const slug = match.params.slug;

    const {...currentUser} = useContext(AuthContext);

    const [comments, setComments] = useState([]);
    const [loading, setLoading] = useState(true);

    useEffect(() => {

        const fetchData = () => {
            const data = firebase.database().ref(`/posts/${slug}/comments`)

            data.once('value')
            .then((snapshot) => {
                if (snapshot) {
                    let comments = [];
                    const snapshotVal = snapshot.val();
                    for (let comment in snapshotVal) {
                        comments.push(snapshotVal[comment]);
                    }
                    setComments(comments);
                    setLoading(false);
                }
            });
        }
        fetchData();
    }, [slug, comments])


    if (loading) {
        return <Loading />;
    };

    const postComment = (values) => {

        console.log(!!currentUser.currentUser)

        if (!!currentUser.currentUser) {
            const comment = {
                commentText: values.commentText,
                commentCreator: currentUser.currentUser.displayName,
                currentUserId: currentUser.currentUser.uid,
            }

            const postRef = firebase.database().ref(`posts/${slug}/comments`);
            postRef.push(comment);

            reset();
        } else {
            toast.error('You are not authenticated ?');
        }
    };

    const deleteComment = () => {

        console.log(comments[0].commentUserId);
        console.log(currentUser.currentUser.uid);

        if (currentUser.currentUser.uid === comments[0].commentUserId) {
            console.log('correct');
        }

        const key = firebase.database().ref(`posts/${slug}/comments`).once('value');

        key.then(snapshot => {
            console.log(snapshot.val());
        }).catch((error) => {
            console.log(error);
        });

    };

    const back = () => {
        history.push('./');
    };

    return (
        <div className='main' style={{ maxWidth: '600px' }}>
            <div className='see-user-comments' onClick={back} style={{ cursor: 'pointer', height: '50px' }}>
                Commenting on the post: {slug}
            </div>
            <div className='see-user-comments' style={{ padding: '10px 0' }}>
                <div>
                    <img src={User} alt='Profile' style={{ width: '30px' }} />
                    <span className='usertag-span'>{currentUser.displayName}</span>
                </div>
                <div>
                    <form onSubmit={handleSubmit(postComment)}>
                        <textarea 
                            name='commentText'
                            rows='3'
                            style={{ margin: '10px 0' }}
                            placeholder='Add to the conversation!'
                            ref={register} 
                        /> 
                        <span style={{ width: '90%' }}>
                            <button>Comment</button>
                        </span>
                    </form>
                </div>
            </div>
            {comments.map((comment, index) =>
            <div key={index} className='see-user-comments' style={{ padding: '15px 0' }}>
                <div style={{ height: '30px' }}>
                    <img src={User} alt='Profile' style={{ width: '30px' }} />
                    <div style={{ flexDirection: 'column', alignItems: 'flex-start', justifyItems: 'center' }}>
                        <span className='usertag-span'>{comment.commentCreator}</span>
                    </div>
                </div>
                <span className='commentText-span'>{comment.commentText}
                    { !!currentUser?.currentUser?.uid === comments[0].commentUserId ?
                        (<button onClick={deleteComment}>Delete</button>) : null 
                    }
                </span>
            </div>
            )}
        </div>
    )
}

export default Comments;

1 个答案:

答案 0 :(得分:1)

我没有看到有问题的错误,只能假设是因为使用以下模式会导致无限循环,因为每次count更改时都会重新触发效果:

const [count, setCount] = useState(0);
useEffect(() => setCount(count + 1), [count]);

comments添加到效果中时,您所做的就是相同的事情。

要解决此问题,您必须更改效果以依靠Firebase的实时事件来更新注释数组。这就像将once('value').then((snap) => {...})更改为on('value', (snap) => {...});一样简单。因为现在它是实时侦听器,所以您还必须返回一个从useEffect调用中取消订阅侦听器的函数。正确执行此操作的最少代码是:

const [postId, setPostId] = useState('post001');

useEffect(() => {
    const postRef = firebase.database().ref('posts').child(postId);
    const listener = postRef.on(
        'value',
        postSnapshot => {
            const postData = postSnapshot.val();
            // ... update UI ...
        },
        err => {
            console.log('Failed to get post', err);
            // ... update UI ...
        }
    )

    return () => postRef.off('value', listener);

}, [postId]);

将这些更改应用于代码(以及一些QoL改进)会产生:

const { register, handleSubmit, reset } = useForm();

const slug = match.params.slug;

const { ...authContext } = useContext(AuthContext); // renamed: currentUser -> authContext (misleading & ambiguous)

const [comments, setComments] = useState([]);
const [loading, setLoading] = useState(true);

let _postCommentHandler, _deleteCommentHandler;

useEffect(() => {
    // don't call this data - it's not the data but a reference to it - always call it `somethingRef` instead
    const postCommentsRef = firebase.database().ref(`/posts/${slug}/comments`);

    // create realtime listener
    const listener = postCommentsRef.on(
        'value',
        querySnapshot => {
            let _comments = [];
            querySnapshot.forEach(commentSnapshot => {
                const thisComment = commentSnapshot.val();
                thisComment.key = commentSnapshot.key; // store the key for delete/edit operations
                _comments.push(thisComment);
            });
            setComments(_comments);
            setLoading(false);
        },
        err => {
            console.log(`Error whilst getting comments for post #${slug}`, err);
            // TODO: handle error
        });

    // update new comment handler
    _postCommentHandler = (formData) => {
        console.log({
            isLoggedIn: !!authContext.currentUser
        });

        if (!authContext.currentUser) {
            toast.error('You are not authenticated ?');
            return;
        }

        const newComment = {
            commentText: formData.commentText, // suggested: commentText -> content
            commentCreator: authContext.currentUser.displayName, // suggested: commentCreator -> author
            currentUserId: authContext.currentUser.uid, // suggested: commentUserId -> authorId
        }

        postCommentsRef.push(newComment)
            .then(() => {
                // commented successfully
                reset(); // reset form completely
            })
            .catch(err => {
                console.log(`Error whilst posting new comment`, err);
                // TODO: handle error
                reset({ commentText: formData.commentText }) // reset form, but leave comment as-is
            })
    }

    // update delete handler
    _deleteCommentHandler = () => {
        if (!comments || !comments[0]) {
            console.log('Nothing to delete');
            return;
        }

        const commentToDelete = comments[0];

        console.log({
            commentUserId: commentToDelete.commentUserId,
            currentUser: authContext.currentUser.uid
        });

        if (authContext.currentUser.uid !== commentToDelete.commentUserId) {
            toast.error('That\'s not your comment to delete!');
            return;
        }

        postCommentsRef.child(commentToDelete.key)
            .remove()
            .then(() => {
                // deleted successfully
            })
            .catch(err => {
                console.log(`Error whilst deleting comment #${commentToDelete.key}`, err);
                // TODO: handle error
            });
    };

    // return listener cleanup function
    return () => postCommentsRef.off('value', listener);
}, [slug]);

const postComment = (values) => _postCommentHandler(values);
const deleteComment = () => _deleteCommentHandler();

由于我将currentUser重命名为authContext,因此还需要更新:

<div>
    <img src={User} alt='Profile' style={{ width: '30px' }} />
    <span className='usertag-span'>{authContext?.currentUser?.displayName}</span>
</div>