持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第14天,点击查看活动详情
react 小项目——步骤条
介绍
本篇文章我们使用 react + ts 来实现一个小项目——步骤条。步骤条在项目当中用到的也很多,我们一般使用的都是组件库封装好的组件,我们拿来直接使用就好了,但是假如需要我们依靠 css + js 来实现一个步骤条呢?
搭建框架
我们先在 react 的src目录下建立一个components文件,我们创建的组件就放在这里面。接下来我们需要搭建我们 react 组件的基本架构
import { ProgressSetpsBox } from "./ProgressSetps.style";
//函数式组件,导出一个函数
export default function ProgressSetps() {
return (); //这里是返回jsx的地方
}
我们这里使用的是 css in js 的方式来进行处理。首先我们需要下载styled-components。
npm install styled-components --save -dev
之后,我们需要在组件文件夹的目录下面创建一个专门用于写 js 的文件ProgressSetps.style。我们在里面引入styled。
// 引入 styled
import styled from "styled-components";
// 设置组件样式
const ProgressSetpsBox = styled.div``;
// 导出组件样式
export { ProgressSetpsBox };
这样我们的基本架构就搭建好了。
tsx 编写
//最开始这个是我们定义的样式组件,他会被替换为一个div
<ProgressSetpsBox className="container mx-auto">
<ul
className="progress-container flex justify-between"
ref={(el) => (ulRef = el)}
>
<li
className="line transition-all delay-100"
ref={(el) => (lineRef = el)}
></li>
{nodeList.map((node: INode) => {
{
/** 遍历节点列表,然后渲染除对应的节点 */
}
return (
<li
className={`nodeItem border-4 text-center transition-all delay-100 ${
node.isActive ? "active" : ""
}`}
key={node.title}
>
{node.title}
</li>
);
})}
</ul>
{/** 上一个 */}
<button
className={`button ${isNext ? "disable" : ""}`}
onClick={(e) => {
changeSetpNext(e);
}}
>
next
</button>
{/** 下一个 */}
<button
className={`button ${isPrev ? "disable" : ""}`}
onClick={(e) => {
changeSetpPrev(e);
}}
>
prev
</button>
</ProgressSetpsBox>
上面我们在return了一些 tsx 代码,他们会被 react 解析成render函数,然后变成 AST 抽象语法树,最后渲染到页面上。
CSS 代码
import styled from "styled-components";
const ProgressSetpsBox = styled.div`
width: 500px;
display: flex;
justify-content: space-around;
align-items: center;
flex-wrap: wrap;
.progress-container {
position: relative;
width: 100%;
margin-bottom: 30px;
.nodeItem {
width: 40px;
height: 40px;
line-height: 32px;
border-radius: 50%;
background-color: #fff;
}
.line {
position: absolute;
top: 50%;
left: 0;
height: 4px;
background-color: skyblue;
z-index: -1;
}
.active {
border-color: skyblue;
}
}
.progress-container::before {
position: absolute;
top: 50%;
left: 0;
content: "";
width: 100%;
height: 4px;
background-color: #ccc;
z-index: -1;
}
.button {
width: 75px;
height: 35px;
border-radius: 5px;
background-color: skyblue;
border: 1px solid skyblue;
}
.disable {
background-color: #ccc;
border: 1px solid #ccc;
}
.disable:hover {
cursor: not-allowed;
}
`;
js 代码
import { useEffect, useState } from "react";
import { ProgressSetpsBox } from "./ProgressSetps.style";
interface INode {
title: number;
isActive: boolean;
}
export default function ProgressSetps() {
const [nodeList, setNodeList] = useState<INode[]>([
{
title: 1,
isActive: true,
},
{
title: 2,
isActive: false,
},
{
title: 3,
isActive: false,
},
{
title: 4,
isActive: false,
},
{
title: 5,
isActive: false,
},
{
title: 6,
isActive: false,
},
]);
const [count, setCount] = useState<number>(0);
//next按钮
const [isNext, setIsNext] = useState<boolean>(false);
//prev按钮
const [isPrev, setIsPrev] = useState<boolean>(false);
//获取进度条
let lineRef: any = null;
//获取节点
let ulRef: any = null;
// 下一个节点
const changeSetpNext = (e: any) => {
if (isNext) {
return;
}
if (isPrev) {
setIsPrev(false);
}
setCount(count + 1);
};
// 对count进行监听操作
useEffect(() => {
if (count == nodeList.length - 1) {
setIsNext(true);
}
if (count == 0) {
setIsPrev(true);
}
// 更改进度条
lineRef.style.width =
Math.floor((1 / (nodeList.length - 1)) * 100 * count) + "%";
// 更改节点
nodeList[count].isActive = true;
setNodeList([...nodeList]);
}, [count, isNext, isPrev]);
//上一个
const changeSetpPrev = (e: any) => {
if (count == 0) {
return;
}
if (isNext) {
setIsNext(false);
}
setCount(count - 1);
nodeList[count].isActive = false;
setNodeList([...nodeList]);
};
}