转载请注明出处,未经同意,不可修改文章内容。
🔥🔥🔥"前端一万小时"两大明星专栏------"从零基础到轻松就业"、"前端面试刷题",已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。
1 需求
本篇我们主要解决上一篇《详情页开发------① "布局"和"数据管理"》末尾的遗留问题。
❓需求:当点击 home 页的数据项(文章列表)时,把点击项的 id
带给"详情页"。
✔️怎么做:动态路由。
2 动态路由
1️⃣打开 home 目录下 components 文件夹里的 Content.js
文件:
jsx
import React, {Component} from "react";
import {Link} from "react-router-dom";
import {
Item,
Cover,
Details,
Title,
Foot,
LoadMore
} from "../style";
import { connect } from "react-redux";
import {actionCreators} from "../store";
class Content extends Component {
render() {
return(
<div>
{
this.props.articleList.map((item, index) => {
return (
<Item key={index}>
<Cover>
{/*
1️⃣-③:❗️同理,由于点击图片也会跳转至相应的"详情页",
故这里的"路径"也要变!
*/}
<Link to={"/detail/" + item.get("id")}><img src={item.get("imgUrl")} alt="" /></Link>
</Cover>
<Details>
{/*
❗️❗️❗️1️⃣-①:在跳转至详情页时,我们可以将其 id 也带上;
<Link to="/detail">
*/}
<Link to={"/detail/" + item.get("id")}> {/* ❗️❗️❗️1️⃣-②:注意写法~ */}
<Title>
{item.get("title")}
</Title>
</Link>
<p>
{item.get("desc")}
</p>
<Foot>
<Link to="/"><span className="username">{item.get("author")}</span></Link>
<span className="iconfont icon-comment"></span><span>{item.get("discuss")}</span>
<span className="iconfont icon-heart"></span><span>{item.get("love")}</span>
<span className="iconfont icon-money"></span><span>{item.get("money")}</span>
</Foot>
</Details>
</Item>
)
})
}
<LoadMore
onClick={() => this.props.getMoreList(this.props.page)}
>
加载更多
</LoadMore>
</div>
)
}
}
const mapStateToProps = (state) => {
return {
articleList: state.getIn(["home", "articleList"]),
page: state.getIn(["home", "articlePage"])
}
}
const mapDispatchToProps = (dispatch) => {
return {
getMoreList(page) {
const action = actionCreators.getMoreList(page);
dispatch(action)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Content);
返回页面查看(当点击 home 页的文章列表某项时,跳转至"详情页",并传递给"详情页"了一个 id
------1):
❌但"详情页"变为空白,不显示了!为什么呢?
2️⃣进入 src 目录下的 App.js
文件:
jsx
import React, { Component } from "react";
import {GlobalStyle} from "./style";
import {GlobalIconStyle} from "./statics/iconfont/iconfont";
import {BrowserRouter, Route} from "react-router-dom";
import Header from "./common/header";
import Home from "./pages/home";
import Detail from "./pages/detail";
import { Provider } from "react-redux";
import store from "./store";
class App extends Component {
render() {
return (
<div>
<GlobalStyle />
<GlobalIconStyle />
<Provider store={store}>
<BrowserRouter>
<div>
<Header />
<Route path="/" exact component={Home}></Route> {/*❗️*/}
{/*
❗️2️⃣-①:由于路径要"完全"匹配 /detail 时,才会进入 Detail 组件。
但 /detail/1 和 /detail 不完全匹配,所以上边会出现空白页面。
可以怎么修改呢?
<Route path="/detail" exact component={Detail}></Route>
*/}
{/*
2️⃣-②:可以改写为 /detail/:id,给路径传递一个额外的"参数"id;
*/}
<Route path="/detail/:id" exact component={Detail}></Route>
</div>
</BrowserRouter>
</Provider>
</div>
);
}
}
export default App;
返回页面查看(详情页正常显示,且有了 id
为 1 这个"参数"):
❓既然"详情页"有了 1 这个"参数",Detail 组件应该怎样去拿到这个"参数"呢?
3️⃣打开 detail 目录下的 index.js
文件:
jsx
import React, {Component} from "react";
import {
DetailWrapper,
Header,
Content
} from "./style.js";
import {connect} from "react-redux";
import {actionCreators} from "./store";
class Detail extends Component {
render() {
// ❗️❗️❗️3️⃣-①:在控制台打印一下 this.props 有些什么东西?
console.log(this.props);
return(
<DetailWrapper>
<Header>{this.props.title}</Header>
<Content
dangerouslySetInnerHTML={{__html: this.props.content}}
/>
</DetailWrapper>
)
}
componentDidMount() {
this.props.getDetail();
}
}
const mapStateToProps = (state) => {
return {
title: state.getIn(["detail", "title"]),
content: state.getIn(["detail", "content"])
}
}
const mapDispatchToProps = (dispatch) => {
return {
getDetail() {
const action = actionCreators.getDetailData();
dispatch(action)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Detail);
3️⃣-②:在 home 页下随便点开一篇文章(我点击的是第 2 篇文章 ),并查看控制台。会打印出一个"对象",点开这个对象,它下边有一个 match
属性,点开 match
, params
下就会有第 2 篇文章 的 id
;
✔️因此,"Detail 组件"可以通过 this.props.match.params.id
来准确地获取到上一个页面传递过来的 id
值。
4️⃣返回 detail 目录下的 index.js
文件:
jsx
import React, {Component} from "react";
import {
DetailWrapper,
Header,
Content
} from "./style.js";
import {connect} from "react-redux";
import {actionCreators} from "./store";
class Detail extends Component {
render() {
return(
<DetailWrapper>
<Header>{this.props.title}</Header>
<Content
dangerouslySetInnerHTML={{__html: this.props.content}}
/>
</DetailWrapper>
)
}
/*
❗️❗️❗️4️⃣-①:页面挂载完成,去异步获取详情页"数据"时,
给这个"异步函数"传递一个"上一页传递过来的 id 值";
*/
componentDidMount() {
this.props.getDetail(this.props.match.params.id); // ❗️❗️❗️
}
}
const mapStateToProps = (state) => {
return {
title: state.getIn(["detail", "title"]),
content: state.getIn(["detail", "content"])
}
}
const mapDispatchToProps = (dispatch) => {
return {
getDetail(id) { /*
4️⃣-②:相应地,这里会拿到一个"参数"id;
*/
const action = actionCreators.getDetailData(id); /*
4️⃣-③:在调用 actionCreators
时,我把这个 id 传给它;
*/
dispatch(action)
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Detail);
4️⃣-④:打开 detail 目录下 store 文件夹中的 actionCreators.js
文件;
javascript
import axios from "axios";
import {CHANGE_DETAIL_DATA_ACTION} from "./actionTypes";
import {fromJS} from "immutable";
const changeDetailDataAction = (result) => ({
type: CHANGE_DETAIL_DATA_ACTION,
title: fromJS(result.title),
content: fromJS(result.content)
})
export const getDetailData = (id) => { // ❗️4️⃣-⑤:接收到这个 id;
return(dispatch) => {
/*
❗️❗️❗️4️⃣-⑥:然后在请求"接口"时,我就可以把这个 id 传给后端;
axios.get("/api/detailData.json")
*/
axios.get("/api/detailData.json?id=" + id) // ❗️❗️❗️
.then((res) => {
const result = res.data.data;
const action = changeDetailDataAction(result);
dispatch(action);
})
.catch(() => {alert("error")})
}
}
返回页面查看(我在 home 页点击的任意文章,其"数据请求"的路径上都会带有各自的 id
参数。如此一来,后端就可以根据这些不同的 id
为我们返回不同的内容 ):
下一篇,我们进入"登录页"的编码,不难,但包含的知识点挺多。
祝好,qdywxs ♥ you!