#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>
)
}