Реакция — самый чистый способ перерисовки компонента при обновлении массива

#javascript #reactjs #typescript #use-effect

#javascript #reactjs #typescript #использование-эффект

Вопрос:

Я новичок в использовании перехватов react, и у меня возникли проблемы с перехватом useEffect из-за следующего:

У меня есть простая доска комментариев для публикации комментариев, а ниже список опубликованных комментариев, и я хочу, чтобы каждый раз, когда создается новый комментарий, компонент перезагружался, чтобы показать его.

введите описание изображения здесь

Эти комментарии получены из API в первом параметре useEffect, проблема заключается во втором параметре (массив зависимостей), если я не ставлю этот параметр, возникает бесконечный цикл, если я помещаю пустой массив, это позволяет избежать бесконечного цикла, но вместо этого мой компонент не отображает новые комментарии, иесли я помещаю переменное состояние «комментарии», это также приводит к бесконечному циклу. Итак, перемещаясь в Интернете, я нашел «решение», которое просто добавляет другое переменное состояние для передачи его в массив зависимостей и изменяется при вызове функции «makecomponent», и таким образом оно обновляет состояние, заставляя мой компонент повторно отображать:

 export const ForumApp = () => {
    const [comment, setComment] = useState("")
    const [comments, setComments] = useState([])
    const [count, setCount] = useState(0)
 
    useEffect(() => {
        const getComments = async () => {
            try {
                const query = await axios.get("http://127.0.0.1:8000/comments")
                setComments(query.data.comments)
            } catch (error) {
                console.log(error);
            }
        }
        getComments()
    }, [count])

    const handleEnter = (e: KeyboardEvent) => {
        if (e.key === ENTER amp;amp; !e.shiftKey) {
            e.preventDefault();
            makeComment();
        }
    };

    const makeComment = async () => {
        try {
            await axios.post("http://127.0.0.1:8000/comments/create", {
                time: "1:29",
                content: comment,
            })

            alert("Comment posted!")
            setCount((prevCount) => prevCount   1)
            setComment("")
        } catch (error) {
            console.log(error)
        }
    };

    return (
        <div className="forum-app">
            <div className="comment-board">
                <h1>Post your comment</h1>
                <textarea
                    placeholder="Write something to us"
                    onChange={(e) => setComment(e.target.value)}
                    onKeyDown={handleEnter}
                    value={comment}
                ></textarea>
                <br />
                <button onClick={makeComment}>Comment</button>
            </div>
            <ol>
                {
                    comments.map(({_id, content}) => (
                        <CommentItem key={_id} content={content}/>
                    ))
                }
            </ol>
        </div>
    )
}
 

Итак, в основном мой вопрос таков: является ли это «правильным» способом сделать это или существует другой более чистый способ без объявления другой переменной и делать это только с тем, что у меня есть?

Ответ №1:

Другой более чистый способ, вероятно, будет использовать redux amp; redux-thunk для отправки действия для выполнения асинхронной выборки. Когда данные извлекаются и хранилище redux обновляется, подключенные компоненты повторно отображаются с последним состоянием из хранилища.

Это много новых движущихся частей, которые нужно добавить в приложение, если оно еще не использует redux. Важным аспектом здесь является необходимый дополнительный «флаг» для запуска другой выборки данных.

Я думаю, что настройка, которую я бы рекомендовал для вашего кода, чтобы сделать его немного более простым, — это фактически определить fetchData состояние, которое включается и выключается в зависимости от состояния выборки данных. Это позволяет вам также отображать любое состояние «загрузки» во время выборки данных.

 export const ForumApp = () => {
    const [comment, setComment] = useState("")
    const [comments, setComments] = useState([])
    const [fetchComments, setFetchComments] = useState(true) // <-- for initial mount call
 
    useEffect(() => {
        const getComments = async () => {
            try {
                const query = await axios.get("http://127.0.0.1:8000/comments")
                setComments(query.data.comments)
            } catch (error) {
                console.log(error);
            } finally {
              setFetchComments(false); // <-- signify fetching complete
            }
        }

        fetchComments amp;amp; getComments(); // <-- fetch comments if true
    }, [fetchComments])

    const handleEnter = (e: KeyboardEvent) => {
        if (e.key === ENTER amp;amp; !e.shiftKey) {
            e.preventDefault();
            makeComment();
        }
    };

    const makeComment = async () => {
        try {
            await axios.post("http://127.0.0.1:8000/comments/create", {
                time: "1:29",
                content: comment,
            })

            alert("Comment posted!")
            setFetchComments(true); // <-- signify start fetching
            setComment("")
        } catch (error) {
            console.log(error)
        }
    };

    return (
        <div className="forum-app">
            <div className="comment-board">
                <h1>Post your comment</h1>
                <textarea
                    placeholder="Write something to us"
                    onChange={(e) => setComment(e.target.value)}
                    onKeyDown={handleEnter}
                    value={comment}
                ></textarea>
                <br />
                <button onClick={makeComment}>Comment</button>
            </div>
            <ol>
                {
                    fetchComments 
                      ? "...fetching comments" // <-- loading state
                      : comments.map(({_id, content}) => (
                        <CommentItem key={_id} content={content}/>
                    ))
                }
            </ol>
        </div>
    )
}
 

Комментарии:

1. Значит, самый чистый способ сделать это — использовать redux? или, по крайней мере, это то, что вы рекомендуете? Я избегал этого, но, думаю, пришло время научиться этому. Спасибо за код, на данный момент это то, что я искал.

2. @AbrahamArreola Я полагаю, что в данном случае «чистый» — это немного субъективно. Лично я думаю, что и то, и другое можно было бы считать «чистым». Используя способ, который у вас был, и в моем ответе он чистый в том смысле, что вся логика содержится в одном блоке, легко читается и представляет собой простой общий шаблон. Redux — это чистый шаблон, управляемый событиями, который позволяет вам абстрагироваться и модулировать общие функции, такие как асинхронная выборка данных и использование общего состояния приложения. В некотором роде все зависит от потребностей и требований вашего приложения. Приветствия и удачи.

3. @Draw Reese Еще раз здравствуйте, в настоящее время я изучил и внедрил redux в свое приложение, но теперь у меня аналогичный вопрос: нужен ли мне дополнительный флаг для запуска выборки или я могу добиться этого с помощью того, что у меня есть? Я попытался передать отправку в массив depency, но это не сработало. Приветствия!

4. @AbrahamArreola Если вы внедрили redux в свое приложение, то шаблон здесь заключается в использовании асинхронных действий через промежуточное программное обеспечение (например, redux-thunk). Логика выборки перемещается в асинхронное действие. Вам не нужно будет использовать какие-либо дополнительные флаги для запуска действия, поскольку вы можете напрямую отправлять асинхронное действие (например, нажатием кнопки) в свой магазин. Если вам нужна дополнительная помощь, не стесняйтесь задать еще один вопрос здесь, на SO, и (у) меня здесь со ссылкой на него, вы, по крайней мере, сможете быстрее увидеть его.

5. я также реализовал асинхронные действия с помощью redux-thunk, и я знаю, что могу отправить любое действие в хранилище, но при этом я хочу добиться того же, о чем я просил в этом сообщении: перечислить все комментарии при отображении компонента и обновить список при создании нового комментария. Я действительно сделал это, поместив отправку в функцию useEffect, но компонент не перерисовывается, когда я добавляю новый комментарий. И теперь мой вопрос в том, нужен ли мне все еще флаг для управления выборкой данных или я могу добиться этого только с помощью методов redux?

Ответ №2:

я предпочитаю использовать user и функционировать как генератор и указывать состояние в качестве параметров, а затем возвращать мой компонент map для создания читаемого кода


таким образом, вам не нужно понимать JSX, чтобы понимать логику кода, и, я думаю, он более гибкий для отладки

 export const ForumApp = () => {
const [comment, setComment] = useState("")
const [comments, setComments] = useState([])
const [count, setCount] = useState(0)

useEffect(() => {
    const getComments = async () => {
        try {
            const query = await axios.get("http://127.0.0.1:8000/comments")
            setComments(query.data.comments)
        } catch (error) {
            console.log(error);
        }
    }
    getComments()
}, [count])

const handleEnter = (e: KeyboardEvent) => {
    if (e.key === ENTER amp;amp; !e.shiftKey) {
        e.preventDefault();
        makeComment();
    }
};

const makeComment = async () => {
    try {
        await axios.post("http://127.0.0.1:8000/comments/create", {
            time: "1:29",
            content: comment,
        })

        alert("Comment posted!")
        setCount((prevCount) => prevCount   1)
        setComment("")
    } catch (error) {
        console.log(error)
    }
};
const commentGenerator = (data) => (data.map(({ _id, content }) => <CommentItem key={_id} content={content} />))

return (
    <div className="forum-app">
        <div className="comment-board">
            <h1>Post your comment</h1>
            <textarea
                placeholder="Write something to us"
                onChange={(e) => setComment(e.target.value)}
                onKeyDown={handleEnter}
                value={comment}
            ></textarea>
            <br />
            <button onClick={makeComment}>Comment</button>
        </div>
        <ol>
            {commentGenerator(comments)}
        </ol>
    </div>
)
 

}

Ответ №3:

итак, если вы хотите загрузить для него объявление, а еще лучше, вы хотите иметь какой-то компонент отложенной загрузки для отложенной загрузки, чтобы повысить производительность при загрузке страницы, вы можете использовать react lazy и suspense для извлечения данных и загрузки компонента комментариев при успешной выборке данных.вы можете загружать компонент, и он автоматически изменяет состояние загрузки и список загрузки компонента.

 import { Suspense, lazy } from 'react';

const CommentItem = lazy(() => import('./CommentItem'));

export const ForumApp = () => {

const getComments = async () => {
    try {
        const query = await axios.get("http://127.0.0.1:8000/comments")
        return query.data.comments.map((commentItem) => <CommentItem {...commentItem} />)
    } catch (error) {
        console.log(error);
        return []
    }
}


return (
    <div className="forum-app">
        <div className="comment-board">
            <h1>Post your comment</h1>
            <textarea
                placeholder="Write something to us"
                onChange={(e) => setComment(e.target.value)}
                onKeyDown={handleEnter}
                value={comment}
            ></textarea>
            <br />
            <button onClick={makeComment}>Comment</button>
        </div>
        <ol>
            <Suspense fallback={<span> loading comments...</span>}>
                {getComments()}
            </Suspense>
        </ol>
    </div>
)
}