【发布时间】:2019-12-18 16:16:25
【问题描述】:
我正在构建一个 React 应用程序,它基本上会加载博客文章,并为每篇文章附加 cmets。
在呈现博客文章时,也会获取该博客文章的 cmets。我还有一个允许提交评论的组件。
单击提交按钮时,我希望 cmets 刷新其数据源,并立即显示新评论。 我如何向我们的 cmets 组件发送某种事件,告诉它发送另一个获取请求?
看来这个问题的核心是这样的:
我如何习惯性地将事件发送到将触发效果的其他 React 组件?
编辑 - 一般解决方案:
- 将 State 提升到最近的组件中
- 创建一个包含状态更新函数的回调函数。将此回调作为道具传递给将触发事件的组件。当事件发生时,在处理程序中运行回调。
Post.js
import React from 'react';
import {useState, useEffect, useContext} from 'react';
import Markdown from 'markdown-to-jsx';
import Container from '@material-ui/core/Container';
import Typography from '@material-ui/core/Typography';
import SendComment from './SendComment';
import Comments from './Comments';
import {POST_URL} from './urls';
import UserContext from './UserContext';
//import CommentListContainer from './CommentListContainer';
export default function Post(props) {
const user = useContext(UserContext);
const [post, setPost] = useState({
content: '',
comments: [],
});
useEffect(() => {
const UNIQUE_POST_URL = [POST_URL, props.location.state.id].join('/');
const fetchPost = async () => {
const result = await fetch(UNIQUE_POST_URL);
const json = await result.json();
setPost(json);
};
fetchPost();
}, [props.location.state.id]);
return (
<div>
<Container>
<Typography
variant="h4"
color="textPrimary"
style={{textDecoration: 'underline'}}>
{post.title}
</Typography>
<Markdown>{post.content}</Markdown>
{post.content.length !== 0 && (
<div>
<Typography variant="h4">Comments</Typography>
<SendComment user={user} posts_id={props.location.state.id} />
<Comments user={user} posts_id={props.location.state.id} />
</div>
)}
</Container>
</div>
);
}
SendComment.js 组件
import React from 'react';
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
import Paper from '@material-ui/core/Paper';
import {COMMENT_SUBMIT_URL} from './urls';
export default function SendComment(props) {
async function handleSubmit(e) {
const comment = document.querySelector('#comment');
// Skip empty comments
if (comment.value === '') {
return;
}
async function sendComment(url) {
try {
const res = await fetch(url, {
method: 'POST',
body: JSON.stringify({
comment: comment.value,
users_id: props.user.users_id,
posts_id: props.posts_id,
}),
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
'Accept-Language': 'en-US',
},
});
comment.value = '';
return res;
} catch (e) {
console.log(e);
}
}
const res = await sendComment(COMMENT_SUBMIT_URL);
if (res.ok) {
// Reload our comment component !
// Here is where we want to send our "event"
// or whatever the solution is
}
}
return (
<Grid container justify="space-evenly" direction="row" alignItems="center">
<Grid item xs={8}>
<TextField
id="comment"
fullWidth
multiline
rowsMax="10"
margin="normal"
variant="filled"
/>
</Grid>
<Grid item xs={3}>
<Button variant="contained" color="primary" onClick={handleSubmit}>
Submit
</Button>
</Grid>
</Grid>
);
}
评论.js
import React from 'react';
import {useState, useEffect} from 'react';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import ListItemAvatar from '@material-ui/core/ListItemAvatar';
import Avatar from '@material-ui/core/Avatar';
import Divider from '@material-ui/core/Divider';
import {timeAgo} from './utils';
import {COMMENT_URL} from './urls';
export default function Comments(props) {
const [comments, setComments] = useState({
objects: [],
});
useEffect(() => {
async function getComments(posts_id) {
const filter = JSON.stringify({
filters: [{name: 'posts_id', op: 'equals', val: posts_id}],
});
try {
COMMENT_URL.searchParams.set('q', filter);
const res = await fetch(COMMENT_URL, {
method: 'GET',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
const json = await res.json();
setComments(json);
} catch (e) {
console.log(e);
}
}
getComments(props.posts_id);
}, [props.posts_id]);
const commentList = comments.objects.map(comment => (
<ListItem key={comment.id} alignItems="flex-start">
<ListItemAvatar>
<Avatar alt={comment.users.name} src={comment.users.picture} />
</ListItemAvatar>
<ListItemText
primary={`${comment.users.name} - ${timeAgo(comment.created_at)}`}
secondary={comment.comment}></ListItemText>
<Divider />
</ListItem>
));
return <List>{commentList}</List>;
}
此代码当前有效,但是新评论仅在页面重新加载时显示,而不是在提交后立即显示。
【问题讨论】:
标签: javascript reactjs events material-ui