跳到主要内容位置

React Props 教程 - 制作一个按钮

张旭乾

你在写 HTML 页面的时候肯定知道,html 标签的属性都是固定的,比如 a 标签的 href, input 里边的 type 属性。这些属性都是内置的,不方便扩展和复用。而用 React 创建组件的话,可以给它定义一些更符合语义和逻辑的属性,比如颜色、尺寸等。这些属性在 React 里边叫做 props,你可以自己定义这些属性将会影响到组件的哪些部分。这样的一个组件,通过给它一个合适的名字,比如 Button,那么所有按钮的展现都可以用它来实现,只需要改变它的属性就可以展示为不同的样式。那今天我就教你定义一个这样的按钮组件,它有默认的背景色、文字颜色、还有实心和线框样式,后边通过属性,props,来控制它是蓝色、红色还是黑色,然后利用另一个属性来控制它是实心的背景还是线框的。好,那咱们开始吧。

你将学到的

上一张效果图:

创建 React 工程

  1. 使用 create-react-app 创建一个工程:

    yarn create react-app react-button-props

    等同于

    npx create-react-app react-button-props
  2. 添加classnames依赖(稍后解释它的作用)

    yarn add classnames

创建 Button 组件

  1. src 下边新建一个 Button 文件夹。组件应该有自己独立的文件夹,所有跟这个组件有关的文件,比如 css、hooks 等都放到一起,这样在查看一个组件源码时,相应的文件都会在一块,方便查找。另外也方便和其他人共享这个组件,它是一个独立的整体。

  2. Button 文件夹创建 index.js 文件,里边放用来定义按钮组件的代码。

  3. Button 文件夹下创建 style.modules.css 文件,在这里咱们先用普通的 css 来定义按钮的样式,后续的教程里我再给你介绍styled-components。它是一个 css-in-js 的解决方案

    CSS modules 的作用

    这个带 modules 的 css 文件使用了 css modules 库,它是 create-react-app 工具里自带的,用来避免全局 class 名字冲突,在普通 css 下,如果不同的样式文件都同时使用了 .button 这样的 class 名,那么后面的就会把前面的覆盖。使用了css modules之后,它会自动生成随机的 class 名字。这样这个组件里边定义的 class 就不会被其他组件定义的同名的 class 给覆盖。当然你也可以不用它,有些全局的 css 可以直接定义在普通的 css 文件里。

  4. 编写 button 组件的代码:

    import React from "react";

    function Button(props) {
    return <button>{props.children}</button>;
    }

    export default Button;

    react 中的组件默认都会被传递一个 props 属性,里边会默认包含一个 children 属性,也就是说在使用 <Button /> 组件时,两个标签中间所有的代码都会当作 children 传递进来。上边的代码,也可以使用 rest 操作符简化:

    function Button({ children }) {
    return <button>{children}</button>;
    }

    比如:

    function App() {
    // 作为演示,我在 App 组件里又定义了一个 Button 组件
    function Button({ children }) {
    return <button>{children}</button>;
    }
    return <Button>按钮</Button>;
    }
  5. 删除 App 的 return 中的所有的代码,导入 Button 组件,然后把它写在 return 里边:

    function App() {
    return <Button>默认按钮</Button>;
    }

编写 Button 默认样式

  1. 打开 Button 组件下的 style.modules.css 文件,写上下边的 css 代码:

    .button {
    padding: 12px 48px;
    border-radius: 24px;
    background-color: #0076ff;
    box-shadow: 0px 4px 10px rgba(135, 167, 171, 0.5);
    font-size: 14px;
    color: white;
    font-weight: 500;
    text-shadow: 0px 0.5px 1px rgba(0, 0, 0, 0.15);
    outline: none;
    cursor: pointer;
    }

    这里把按钮设置了背景、圆角边框、字体、指针样式和阴影。

  2. 打开 Button 组件的 index.js 文件,导入 css 文件并赋值给一个变量,这里叫 styles

    import styles from "./styles.module.css";
  3. 给 Button 组件加上 className 属性,这里可以用 styles.button 来访问 css 文件中的 .button 的样式:

    <button className={styles.button}>{children}</button>

    可以在页面上看到,这个默认按钮的样式已经加载好了。

用 Props 给 Button 不同的样式

在给 Button 写完默认样式之后,咱们再来定义它几个变体,比如红色、黑色。你可以在 Button 里多添加一个 color 属性,代表其他组件使用它时,可以传递一个 color 属性,Button 会根据它的值显示不同的颜色,这里我假设它有三种,一种是默认的蓝,就是不传 color 的时候的颜色,一种是红色,color 的值为 red 的时候显示,一种是黑色,在 color 为 black 的时候显示。首先咱们先把这两种颜色的 css 样式定义好:

.red {
background-color: #ff4059;
}

.black {
background-color: #2e3434;
}

classnames 组合样式

在定义完这两个额外的样式之后,需要把它和.button定义的样式组合起来。这里可以手动去拼,也可以用之前刚开始安装的 classnames 依赖库。它可以根据条件来组合 className,只有满足一定条件的 class 才会被组合进来,在咱们这里,可以这样使用:

<button
className={classNames(styles.button, {
[styles.red]: color === "red",
[styles.black]: color === "black"
})}
>
{children}
</button>

它接收多个参数,第一个直接传递了 styles.button 这个 class,说明它无论其它属性怎么变化,它都是要有的,最后传递了一个对象,对象的 key 是 styles 中的 class 的名字,value 是 boolean 类型的,需要一个条件,返回 true 就把这个 class 加到组合中,false 就不加。那这里,如果 color 的值是 red,那么 button 中就会有 .red 定义的样式。

再在 App.js 中添加两个按钮 ,一个 color 设置为 red,一个 color 设置为 black:

<main>
<div>
<Button>默认按钮</Button>
<Button color="red">红色按钮</Button>
<Button color="black">黑色按钮</Button>
</div>
</main>

需要注意的是,react 要求在返回的 JSX 中只能有一个顶级的标签,不能有并列的多个,比如不能同时写三个 button,需要把它们包装在一个大的标签下,这里我用了一个 main,作为内容容器,里边有一个 div 是 button 的容器。

线框样式

如果再加一组线框样式的按钮呢?很简单,我再加一个type属性,默认是primary的,主要按钮,线框按钮的 type 叫它secondary 次要按钮,然后 classnames 添加一个新的 class,在 type 为 secondary 的时候追加到组合中:

function Button({ children, type = "primary", color = "blue" }) {
return (
<button
className={classNames(styles.button, {
[styles.red]: color === "red",
[styles.black]: color === "black",
[styles.secondary]: type === "secondary"
})}
>
{children}
</button>
);
}

接下来定义 secondary 样式:

.secondary {
background: none;
border: 2px solid #0076ff;
color: #0076ff;
}

.secondary.red {
border-color: #ff4059;
color: #ff4059;
}

.secondary.black {
border-color: #2e3434;
color: #2e3434;
}

在这里我把默认蓝色、红色、黑色按钮的边框和文字都设置了不同的颜色。

显示所有按钮样式

在 App.js 中咱们显示所有不同属性的按钮,然后给按钮容器加上一个 className,用来对按钮进行排版,这里我直接用了普通的 css 样式,也就是创建工程时给生成好的 App.css,直接把它导入进来:

App.js

// 省略函数定义
<main>
<div className="btn__container">
<Button>默认按钮</Button>
<Button color="red">红色按钮</Button>
<Button color="black">黑色按钮</Button>
<Button type="secondary">线框按钮</Button>
<Button type="secondary" color="red">
线框按钮
</Button>
<Button type="secondary" color="black">
线框按钮
</Button>
</div>
</main>

App.css

main {
width: 100vw;
height: 100vh;
background-color: #f2f2f2;
display: flex;
align-items: center;
justify-content: center;
}

.btn__container {
display: grid;
grid-template-columns: repeat(3, 1fr);
column-gap: 24px;
row-gap: 24px;
align-content: center;
justify-items: center;
}

好了,这个使用 React Props 来展示不同样式的按钮到这里就结束了,里边几个概念:

  • React 组件默认会传递 Props 参数
  • 使用 props 可以传递任何自定义的属性
  • 组件相关的文件都放到一个文件夹里
  • css modules - 用来生成随机局部 class 名字
  • classnames - 用来组合多个 class

你学会了吗?如果有问题,欢迎通过下方链接参与讨论。

提示

一系列的课程让你成为高级前端工程师。课程覆盖工作中所有常用的知识点和背后的使用逻辑,示例全部都为工作项目简化而来,学完即可直接上手开发!

即使你已经是高级前端工程师,在课程里也可能会发现新的知识点和技巧,让你的工作更加轻松!

《React 完全指南》课程,连载中现只需 48 元(领取优惠券)点击查看详情。

《Vue 3.x 全家桶完全指南与实战》课程,包括 Vue 3.x、TypeScript、Vue Router 4.x、Vuex 4.x 所有初级到高级的语法特性详解,让你完全胜任 Vue 前端开发的工作。点击查看详情。

《React即时通信UI实战》课程,利用 Storybook、Styled-components、React-Spring 打造属于自己的组件库。

《JavaScript 基础语法详解》本人所著图书,包含 JavaScript 全面的语法知识和新特性, 可在京东、当当、淘宝等各大电商购买