react 小项目——步骤条

478 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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]);
  };
}