详情组件
之前我们编写了ArticleDetail
这个组件,不过它目前只能显示article+ID
这样一串文本,让我们修改代码,让这个组件显示实际的文章内容吧。
首先目前我们点击首页的文章标题,指向的url为articles/id/
,这与我们后端的url重复了。为此,更改一下backend/urls.py
:
urlpatterns = [
path('admin/', admin.site.urls),
# 全都放到api这个路径后面
path('api/', include('article.urls')),
path('auth/', include('rest_framework_social_oauth2.urls')),
]
注意之前ArticleList
组件中请求API的地方要做出修改哦。
接着修改ArticleDetail.js
:
import React, {useEffect, useState} from "react";
import { useParams } from 'react-router-dom';
const ArticleDetail = () => {
// HOOK
const { articleId } = useParams();
const [loading, setLoading] = useState(true);
const [article, setArticle] = useState({});
const fetchArticle = () => {
fetch(`/api/articles/${articleId}/`)
.then(response => response.json())
.then(result => {
setArticle(result);
setLoading(false);
})
.catch(e => console.log(e));
}
useEffect(fetchArticle, [])
return (
loading
?
加载中...
:
{article.title}
{article.body}
)
}
export default ArticleDetail
代码很烂,仅供参考。之前我们讲过类组件,只有类组件有生命周期,有state
,但是现在React
有了HOOK
这一功能,看我们的代码中,以use
开头的地方,都是HOOK
。
useState
使函数组件可以像类组件一样使用state
,它使用一个参数作为初始值,并返回一个数组,例如代码中第7行loading
部分,我们给了名为loading
的state
初始值false
,返回的数组中第一个元素是loading
这个state
,而第二个则是一个函数,可以用于更改state
值。
之后我们定义一个获取文章的函数,获取结果后设置article
,并将loading
状态设为false
。useEffect
则类似类组件的componentDidMount
这个生命周期函数,在组件挂载后,将会执行通过参数传递的回调函数,获取文章数据。第二个参数[]
使得这个函数只在组件挂载后运行一次。
有关HOOK
的详情可以去看React
官方文档。
返回值部分的JSX
就不说了,只是三元运算符根据loading
状态做条件渲染,没有特别的地方。
动态字段
刚刚这个详情页面,其实主要是为了说明一种情况,对于后端定义的API,并不是在任何地方都需要全部数据的。例如,在首页我们只需要文章标题、发布时间,详情页才显示文章内容。尽管现在硬件条件越来越好,但如果把所有数据都通过网络传输到客户端,无疑是对网络资源的浪费。
不过如果为前端不同页面定义不同的序列化器或者修改模型那就太麻烦了,我们可以通过定义请求中的参数如fields=xx,xxx
来实现这一点,已经有一个好用的第三方库帮我们做到啦。那就是drf-flex-fields,让我们使用pip来安装:
$ pip install drf-flex-fields
要使用也非常简单,只需要修改article/serializers.py
:
from rest_flex_fields import FlexFieldsModelSerializer
from article.models import Article
from rest_framework import serializers
class ArticleSerializer(FlexFieldsModelSerializer):
author = serializers.ReadOnlyField(source='author.username')
class Meta:
model = Article
fields = ['id', 'author', 'title', 'body', 'created', 'updated']
用命令行测试一下:
$ http http://127.0.0.1:8000/api/articles/\?fields\=author,body
...
[
{
"author": "elliot",
"body": "时间测试"
},
{
"author": "elliot",
"body": "现在是4月5日11点10分"
}
]
$ http http://127.0.0.1:8000/api/articles/\?omit\=author,body
...
[
{
"created": "2020-04-05T11:47:49.087547+08:00",
"id": 2,
"title": "现在时间",
"updated": "2020-04-05T11:47:49.087580+08:00"
},
{
"created": "2020-04-05T11:10:56.880622+08:00",
"id": 1,
"title": "第一天",
"updated": "2020-04-05T11:10:56.880674+08:00"
}
]
既可以通过fields
参数来指定需要的字段,也可以通过omit
来指定要排除哪些字段。注意url中参数要用问号?
开头,在命令行中则要用\?
来转义问号。
这时候可以去修改前端代码:
......
const ArticleItem = props => (
......
{/* 删掉这行{props.item.body}*/}
......
)
class ArticleList extends Component {
......
componentDidMount() {
// 修改这里
fetch('/api/articles/?omit=author,body')
.then(response => response.json())
.then(result => this.setState({articleList: result}))
.catch(e => this.setState({error: e}));
}
......
}
......
现在首页就没有文章详细内容了。文章详情组件就留给读者自己去修改了。
drf-flex-fields
这个库的功能可不止于此哦,建议读者去Github看看这个库的说明与源码,可以使我们的API便捷的实现关系型字段的联合查询。
最后
最近对这个系列的文章有些思考,在这里不打算一步一步做一个实际的,或者说好看的,优雅的博客系统,而是想到一些功能,尽量加上去,实践Django与React的结合,如果有读者希望我实现什么功能,可以在公众号留言,最终我们可能会做出一个很奇葩的应用,哈哈。如果想要直接复制代码做一个个人博客,可以参考我的个人博客,Github地址:https://github.com/Eliot00/el...
欢迎关注我的公众号“公子政的宅日常”,原创技术文章第一时间推送。