(16)详情页开发——② 动态路由(页面路由参数的传递) | React.js 项目实战:PC 端“简书”开发

复制代码
转载请注明出处,未经同意,不可修改文章内容。

🔥🔥🔥"前端一万小时"两大明星专栏------"从零基础到轻松就业"、"前端面试刷题",已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。

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">&#xe602;</span><span>{item.get("discuss")}</span>
                    <span className="iconfont icon-heart">&#xe8f4;</span><span>{item.get("love")}</span>
                    <span className="iconfont icon-money">&#xe607;</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!

相关推荐
卸任4 小时前
Vibe coding工具使用心得
前端·ai编程
brien94 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance5 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M5 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写5 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李5 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易5 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
清晨Coding5 小时前
删了 Cookie 还能自动登录?Google系网站登录态自动恢复的原理分析
javascript
风骏时光牛马5 小时前
前沿视界
前端
kyriewen5 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员