[项目实战,源码完整]手把手教你怎么封装组件,React 重写学成在线 III_react 函数组件如何重写(1)

        <a href={val.url}>{val.title}</a>
      </li>
    ))}
  </ul>
);

};

return (


{subHeaderName}


{getMidContent()}


);
};


到这一步,距离 sub header 实现完毕还差最后一步——选中高亮的实现。


以下为静态的实现效果:


![](https://img-blog.csdnimg.cn/20210605232021261.png)
##### 使用 useState 去实现选中高亮


选中高亮的逻辑实现起来其实并不复杂,具体步骤如下:


1. 需要一个变量保存当前选中的选项,默认为 `热门`
2. 在点击事件发生时,需要替换被选中的选项


这个部分的实现就需要借助 React 自带的一个钩子函数:`setState`,`setState` 的存在是为了替换 类组件(class-based component) 中的 状态(state) 属性。


语法为:



> 
> 建议使用 ES6 中的 const  
>  const [property, propertyHandler] = useState(defaultValue);
> 
> 
> 


其中


* `property` 为该属性的变量名
* `propertyHandler` 为事件发生时用来更新状态的函数

 这个函数只是一个标识符,它不负责处理其他的逻辑,只负责更新传进去的值
* `useState` 中传进去的为默认值
* 在需要更新状态的事件函数中调用 `propertyHandler` 即可。


以本业务场景为例,初始值的代码如下;



const [activeTitle, setTitle] = useState(midConent[0].title);

// 点击事件
function clickHandler(e, val) {
// 防止新页面打开 and/or 重定向的发生
e.preventDefault();
// 更新状态
setTitle(val);
}


* `activeTitle` 定义的是当前被选中,需要被设为高亮的选项
* `setTitle` 为 点击标签时设置当前高亮选项 的函数

 它会接受一个值,并且直接替换 `activeTitle`
* `useState` 设置的是默认值,在这个情况下也就是热门——第一个选项的标题


点击事件 `clickHandler` 会还给每一个 a 标签,在 a 标签被鼠标点击后就会触发这个状态,更新当前显示高亮的标签。


实现后的动态效果:


![](https://img-blog.csdnimg.cn/20210606094813242.gif)
到这一步,标题的实现就完成了。


完整 JSX 代码如下:


* SubHeader

 

import React, { useState } from ‘react’;
import PropTypes from ‘prop-types’;

const SubHeader = (props) => {
// =新增部分===
const [activeTitle, setTitle] = useState(midConent[0].title);

function clickHandler(e, val) {
e.preventDefault();
setTitle(val);
}
// =新增部分===
const getMidContent = () => {
return (


  • {midConent.map((val) => {
    const { title } = val;
    return (

  • <a
    href={val.url}
    onClick={(e) => clickHandler(e, title)}
    className={activeTitle === title ? ‘selected’ : null}
    >
    {title}


  • );
    })}

);
};
};

export default SubHeader;

* Home

 

const Home = () => {
return (










);
};



#### 精品推荐和课程推荐 的实现


标题已经实现了,剩下的内容就是一排排的课程:


![](https://img-blog.csdnimg.cn/20210610215822583.png)
很明显,每一个课程的结构都是一样的:


* 课程图片
* 课程标题
* 课程描述
* 新款课程(右上角的小标题)
* 爆款课程(右上角的小标题)


那么,第一步可以先将单独的 课程 封装起来,随后再循环遍历所有的课程列表,就能够获得一排排的课表了。


##### 封装课程组件


这就是之前在 common 里就设计好的 CourseItem 组件。


根据上面列出的课程结构,所以课程组件会需要获取以下这些属性:


* id,上文没显示的内容,这里是作为一个标识符而存在的
* title, 标题,也就是课程名称
* img, 课程图片
* peopleStudying,多少人在学习,属于课程描述的内容
* courseLevel,初级中级高级,也属于课程描述的内容
* isNew 或 isHot,用或是因为 PSD 的设计中只显示了一个,不过具体的检查我偷懒了,也没做


###### 课程主体


暂时不考虑 isNew 或 isHot,先将主体结构实现:



import React from ‘react’;

const CourseItem = (props) => {
const { peopleStudying, courseLevel, id, title, img, isNew, isHot } = props;

return (



{title}


{courseLevel}· {peopleStudying}
人正在学习



);
};

export default CourseItem;


随后放入假数据:



import php from ‘…/asset/img/courses/php.png’;

class Course {
// 偷一下懒,学习人数和等级写死了
// 也可以传到构造函数中去,但是PSD上都一样,我就……
peopleStudying = 1125;
courseLevel = ‘高级’;
constructor(id, title, img, isNew = false, isHot = false) {
this.id = id;
this.title = title;
this.img = img;
this.isNew = isNew;
this.isHot = isHot;
}
}

const coursePhp = new Course(1, ‘test’, php, false, true);

// course list for home page
export const courseSuggestion1 = [coursePhp];


和 CSS:



.course-item {
width: 228px;
height: 270px;
background-color: #fff;
margin: 0 15px 15px 0;
}
.course-img {
width: 100%;
}
.course-item h4 {
margin: 20px;
font-size: 14px;
color: #050505;
font-weight: 400;
}
.course-info {
margin: 0 20px;
font-size: 12px;
color: #999;
}
.course-level {
color: #ff2c2d;
}


这样一来,效果就已经有了:


![](https://img-blog.csdnimg.cn/20210606132546177.png)
###### 完整实现课程


引入之前拉下来的两个图片,这里偷懒了没有检查排他性——即只能存在 `isNew` 或 `isHot`,而不能二者同时存在。


正式开发中如果有需求的话,这个检查时一定要做的。


然后利用三元表达是去检查 `isNew` 和 `isHot` 是否为 `true`,如果是的话就渲染对应的组件,如果不是的话就渲染 null。


又因为两个图标的效果是完全一致的,我这里继续抽了一个函数出来去实现 icon:



import React from ‘react’;
import ‘./courseItem.css’;
import hotLabel from ‘…/…/asset/img/courses/hot.png’;
import newLabel from ‘…/…/asset/img/courses/new.png’;

const getLabelImg = (img, label) => {
return {label};
};

const CourseItem = (props) => {
const { peopleStudying, courseLevel, id, title, img, isNew, isHot } = props;

const isNewCourse = isNew
? getLabelImg(newLabel, ‘new-course absolute’)
: null;
const isHotCourse = isHot
? getLabelImg(hotLabel, ‘hot-course absolute’)
: null;

return (


{title}
{title}


{courseLevel}· {peopleStudying}
人正在学习


{isNewCourse}
{isHotCourse}

);
};

export default CourseItem;


实现效果:


![](https://img-blog.csdnimg.cn/20210606133624532.png)
有点这个意思了。


##### 添加课程列表的数据


一个个手动复制黏贴就是非常浪费时间的事情了,所以下一步就是封装伪数据。


这里主要就是创建一个课程的类,这样可以快速的实例化课程,以及创造几个假数据:



import php from ‘…/asset/img/courses/php.png’;
import andriod from ‘…/asset/img/courses/andriod.png’;
import angular from ‘…/asset/img/courses/angular.png’;
import androidHybrid from ‘…/asset/img/courses/andriod-hybrid.png’;

class Course {
// 偷一下懒,学习人数和等级写死了
// 也可以传到构造函数中去,但是PSD上都一样,我就……
peopleStudying = 1125;
courseLevel = ‘高级’;
constructor(id, title, img, isNew = false, isHot = false) {
this.id = id;
this.title = title;
this.img = img;
this.isNew = isNew;
this.isHot = isHot;
}
}

const coursePhp = new Course(
1,
‘Think PHP 5.0 博客系统实战项目演练’,
php,
false,
true
);
const courseAndriod = new Course(
2,
‘Android 网络图片加载框架详解’,
andriod,
true
);
const courseAngular = new Course(
3,
‘Angular 2 最新框架+主流技术+项目实战’,
angular
);
const courseAndroidHybrid = new Course(
4,
‘Android Hybrid APP开发实战 H5+原生!’,
androidHybrid
);
const courseAndroidHybrid2 = new Course(
5,
‘Android Hybrid APP开发实战 H5+原生!’,
androidHybrid
);

// course list for home page
export const courseSuggestion1 = [
coursePhp,
courseAndriod,
courseAngular,
courseAndroidHybrid,
courseAndroidHybrid2,
];


第一组伪数据就实现好了,接下来在 CourseSuggestion 组建中引入这个列表中,进行循环遍历出所有的课程:



import React from ‘react’;
import SubHeader from ‘…/subHeader’;
import { subHeaderOl, checkMore } from ‘…/…/…/constants/home’;
import { courseSuggestion1 } from ‘…/…/…/constants/courseList’;
import CourseItem from ‘…/…/…/common/courseItem’;

// 渲染课程列表,不含其他的内容模块
const CourseSuggestion = (props) => {
return (




{courseSuggestion1.map((course) => (
<CourseItem {…course} key={course.id} />
))}


);
};

export default CourseSuggestion;


实现效果如下:


![](https://img-blog.csdnimg.cn/20210606134717905.png)
除了差点 CSS 之外,这个味儿对了。


useHistory是react-router-dom提供的一个钩子函数,可以用来通过它实例化一个history对象,用来实现页面的重定向。


使用方法如下:



// 引用钩子函数
import { useHistory } from ‘react-router-dom’;

// 实例化一个history对象
const history = useHistory();
// 通过将 url 推入 history 完成重定向的操作
history.push(url);


所以,最后在每个课程列表上加入 `history.push(url/courseId)` 完成重定向。这样,当用户点击单独的课程列表后,就会被重定向到对应的课程页面去:


![](https://img-blog.csdnimg.cn/20210606135855412.gif)
当然,课程页面还没有实现,所以看到的就是空白的。


新增的 Course-Item 代码如下:



// 新增
import { useHistory } from ‘react-router-dom’;

const CourseItem = (props) => {
// =新增=====
const history = useHistory();

const redirectToCourseDetail = () => {
history.push(${COURSES}/${id});
};
// =新增=====

return (
// =修改==


{/* = 修改== */}
{title}
{title}


{courseLevel} · {peopleStudying}
人正在学习


{isNewCourse}
{isHotCourse}

);
};

export default CourseItem;


### 源码


源码依旧被打包上传了:[学成在线-react-part3](https://bbs.csdn.net/topics/618545628),或者也可以直接到我的 github 上去拉:[xczx-react](https://bbs.csdn.net/topics/618545628)。


### 总结


这次实现的模块为以下几个被红线画出来的模块:




![img](https://img-blog.csdnimg.cn/img_convert/a4ca8764f6ad645aa974a9c8bff7f4ff.png)
![img](https://img-blog.csdnimg.cn/img_convert/63e31ca3164e6eff552c9d58f10ac88c.png)
![img](https://img-blog.csdnimg.cn/img_convert/8347b8de9378e6f1b2deb4147034316c.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**

源码

源码依旧被打包上传了:学成在线-react-part3,或者也可以直接到我的 github 上去拉:xczx-react

总结

这次实现的模块为以下几个被红线画出来的模块:

[外链图片转存中…(img-k73u1JTl-1714715937182)]
[外链图片转存中…(img-RabnKc3O-1714715937183)]
[外链图片转存中…(img-SfcWbcd3-1714715937184)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值