30-seconds-of-react 项目的中文翻译版本,使用 umi.js 对所有案例进行分析、注释、上线。

共 25 个组件,目前已全部翻译完成 。

Logo

React 30 秒速学

精选有用的 React 片段,你可以在30秒或更短的时间内理解。

  • 使用 Ctrl + F command + F 搜索片段。
  • 欢迎贡献(原项目),请阅读30-seconds-of-react 贡献指南
  • 片段用React 16.8+编写,充分使用 hooks 特性。

先决条件

要将代码段导入项目,必须导入React并复制粘贴组件的JavaScript代码,如下所示:

  1. import React from 'react';
  2. function MyComponent(props) {
  3. /* ... */
  4. }

如果有任何与您的组件相关的CSS,请将其复制粘贴到具有相同名称和相应扩展名的新文件,然后将其导入如下:

  1. import './MyComponent.css';

要渲染组件,请确保在元素中存在一个名为“root”的节点 ( 最好是<div> ) 并且导入了ReactDOM,如下所示:

  1. import ReactDOM from 'react-dom';

umi.js 的应用

在 React 30 秒速学这个中文翻译项目里,我使用 umi.js 进行代码的学习。优势是无需定义路由,新建文件即可访问。

把示例代码跑起来:

  1. npm install
  2. npm run dev

相关产品

目录

Array渲染数组

Input输入

Object对象渲染

String字符串处理

Visual视觉效果渲染


Array渲染数组

DataList渲染为列表

通过数组渲染元素列表。

  • 使用 isOrdered prop 的值有条件地渲染<ol><ul>列表。
  • 使用Array.prototype.mapdata中的每个项目渲染为<li>元素,给它一个由其索引和值的串联产生的key
  • 默认情况下,省略 isOrdered prop 以渲染<ul>列表。
  1. function DataList({ isOrdered, data }) {
  2. const list = data.map((val, i) => <li key={`${i}_${val}`}>{val}</li>);
  3. return isOrdered ? <ol>{list}</ol> : <ul>{list}</ul>;
  4. }

例子

  1. export default function() {
  2. const names = ['John', 'Paul', 'Mary'];
  3. return (
  4. <div>
  5. 无序列表:
  6. <DataList data={names} />
  7. 有序列表:
  8. <DataList data={names} isOrdered />
  9. </div>
  10. );
  11. }

ps:

DataTable渲染为表格

通过数组渲染表格,动态创建每一行。

渲染一个带有两列(IDValue)的<table>元素。 使用Array.prototype.mapdata中的每个项目渲染为<tr>元素,由其索引和值组成,给它一个由两者串联产生的key

  1. function DataTable({ data }) {
  2. return (
  3. <table>
  4. <thead>
  5. <tr>
  6. <th>ID</th>
  7. <th>Value</th>
  8. </tr>
  9. </thead>
  10. <tbody>
  11. {data.map((val, i) => (
  12. <tr key={`${i}_${val}`}>
  13. <td>{i}</td>
  14. <td>{val}</td>
  15. </tr>
  16. ))}
  17. </tbody>
  18. </table>
  19. );
  20. }

例子

  1. export default function() {
  2. const people = ['John', 'Jesse'];
  3. return <DataTable data={people} />;
  4. }

ps:

MappedTable渲染为映射表格

通过对象数组渲染表格,属性名称与列对应,动态创建每一行。

  • 使用Object.keys()Array.prototype.filter()Array.prototype.includes()Array.prototype.reduce()生成一个filteredData数组,包含所有对象 使用propertyNames中指定的键。
  • 渲染一个<table>元素,其中一组列等于propertyNames中的值。
  • 使用Array.prototype.mappropertyNames数组中的每个值渲染为<th>元素。
  • 使用Array.prototype.mapfilteredData数组中的每个对象渲染为<tr>元素,对象中的每个键包含一个<td>
  1. function MappedTable({ data, propertyNames }) {
  2. let filteredData = data.map(v =>
  3. Object.keys(v)
  4. .filter(k => propertyNames.includes(k))
  5. // 使用 reduce 迭代,为 acc 对象赋值:
  6. // 回调函数为 (acc, key) => ((acc[key] = v[key]), acc) 初始值为 {}
  7. // ((操作), 返回值) 语法解读:括号里进行任意操作,并指定返回值
  8. .reduce(( acc, key) => ((acc[key] = v[key]), acc), {}),
  9. );
  10. return (
  11. <table>
  12. <thead>
  13. <tr>
  14. {propertyNames.map(val => (
  15. <th key={`h_${val}`}>{val}</th>
  16. ))}
  17. </tr>
  18. </thead>
  19. <tbody>
  20. {filteredData.map((val, i) => (
  21. <tr key={`i_${i}`}>
  22. {propertyNames.map(p => (
  23. <td key={`i_${i}_${p}`}>{val[p]}</td>
  24. ))}
  25. </tr>
  26. ))}
  27. </tbody>
  28. </table>
  29. );
  30. }

Notes

此组件不适用于嵌套对象,如果在propertyNames中指定的任何属性中有嵌套对象,则会中断。

例子

  1. export default function() {
  2. const people = [
  3. { name: 'John', surname: 'Smith', age: 42 },
  4. { name: 'Adam', surname: 'Smith', gender: 'male' },
  5. ];
  6. const propertyNames = ['name', 'surname', 'age'];
  7. return <MappedTable data={people} propertyNames={propertyNames} />;
  8. }

ps:

Input输入

Input基础输入框

输入框组件,使用回调函数将其值传递给父组件。

  • 使用对象解构来设置<input>元素的某些属性的默认值。
  • 使用适当的属性渲染一个<input>元素,并使用onChange事件中的callback函数将输入值传递给父元素。
  1. function Input({ callback, type = 'text', disabled = false, readOnly = false, placeholder = '' }) {
  2. return (
  3. <input
  4. type={type}
  5. disabled={disabled}
  6. readOnly={readOnly}
  7. placeholder={placeholder}
  8. // event.target.value
  9. onChange={({ target: { value } }) => callback(value)}
  10. />
  11. );
  12. }

例子

  1. export default function() {
  2. return <Input type="text" placeholder="Insert some text here..." callback={val => console.log(val)} />;
  3. }

ps:

LimitedTextarea限制字符数的多行文本

呈现限制字符数的多行文本组件。

  • 使用React.useState() hook 创建content状态变量并将其值设置为value。 创建一个方法setFormattedContent,如果它比limit长,它会修剪输入的内容。
  • 使用React.useEffect() hook 来调用content状态变量值的setFormattedContent方法。
  • 使用<div>来包装<textarea><p>元素,它显示字符数并绑定<textarea>onChange事件来调用setFormattedContent处理 event.target.value的值。

参考: hook文档

  1. import React from "react";
  2. function LimitedTextarea({ rows, cols, value, limit }) {
  3. // React.useState(初始值) 通过在函数组件里调用它,来给组件添加一些内部 state
  4. // 返回一对值:当前状态和一个让你更新它的函数,你可以在事件处理函数中或其他一些地方调用这个函数。
  5. const [content, setContent] = React.useState(value);
  6. const setFormattedContent = text => {
  7. console.log("setFormattedContent");
  8. // 符合长度才允许修改
  9. text.length > limit ? setContent(text.slice(0, limit)) : setContent(text);
  10. };
  11. // useEffect 就是一个 Effect Hook ,在组件挂载和更新时执行
  12. // 可以看做 componentDidMount,componentDidUpdate 和 componentWillUnmount 这三个生命周期函数的组合
  13. React.useEffect(() => {
  14. console.log("useEffect");
  15. setFormattedContent(content);
  16. }, []);
  17. return (
  18. <div>
  19. <textarea
  20. rows={rows}
  21. cols={cols}
  22. onChange={event => setFormattedContent(event.target.value)}
  23. value={content}
  24. />
  25. <p>
  26. {content.length}/{limit}
  27. </p>
  28. </div>
  29. );
  30. }

例子

  1. export default function() {
  2. return <LimitedTextarea limit={32} value="Hello!" />;
  3. }

ps:

LimitedWordTextarea限制单词数的多行文本

呈现限制单词数的多行文本组件。

  • 使用React.useState() hook 创建contentwordCount状态变量,并将它们的值分别设置为value0
  • 创建一个方法setFormattedContent,它使用String.prototype.split(' ')将输入转换为单词数组,并使用 Array.prototype.filter(Boolean)检查 length是否比 limit长。
  • 如果上述length超过limit,则修剪输入,否则返回原始输入,在两种情况下都相应地更新contentwordCount
  • 使用React.useEffect() hook 来调用content状态变量值的setFormattedContent方法。
  • 使用<div>来包装<textarea><p>元素,它显示字符数并绑定<textarea>onChange事件来调用setFormattedContent 处理 event.target.value的值。
  1. function LimitedWordTextarea({ rows, cols, value, limit }) {
  2. const [content, setContent] = React.useState(value);
  3. const [wordCount, setWordCount] = React.useState(0);
  4. const setFormattedContent = text => {
  5. let words = text.split(" ");
  6. // words.filter(Boolean).length 获取数组长度
  7. // .filter(Boolean) 等价于 .filter((item) => {return Boolean(item)})
  8. // 也就是说这样写的意思就是去除数组中为 “假” 的元素
  9. if (words.filter(Boolean).length > limit) {
  10. setContent(
  11. text
  12. .split(" ")
  13. .slice(0, limit)
  14. .join(" ")
  15. );
  16. setWordCount(limit);
  17. } else {
  18. setContent(text);
  19. setWordCount(words.filter(Boolean).length);
  20. }
  21. };
  22. React.useEffect(() => {
  23. setFormattedContent(content);
  24. }, []);
  25. return (
  26. <div>
  27. <textarea
  28. rows={rows}
  29. cols={cols}
  30. onChange={event => setFormattedContent(event.target.value)}
  31. value={content}
  32. />
  33. <p>
  34. {wordCount}/{limit}
  35. </p>
  36. </div>
  37. );
  38. }

例子

  1. export default function() {
  2. return <LimitedWordTextarea limit={5} value="Hello there!" />;
  3. }

ps:

MultiselectCheckbox复选框

呈现一个复选框列表,该列表使用回调函数将其选定的值/值传递给父组件。

  • 使用React.setState()创建一个data状态变量,并将其初始值设置为等于options
  • 创建一个函数toggle,用于切换checked以更新data状态变量,并调用通过组件的props传递的onChange回调。
  • 渲染一个<ul>元素并使用Array.prototype.map()data状态变量映射到单独的<li>元素,其中<input>元素作为它们的子元素。
  • 每个<input>元素都有type ='checkbox'属性并被标记为readOnly,因为它的click事件由父<li>元素的onClick处理程序处理。
  1. const style = {
  2. listContainer: {
  3. listStyle: 'none',
  4. paddingLeft: 0
  5. },
  6. itemStyle: {
  7. cursor: 'pointer',
  8. padding: 5
  9. }
  10. };
  11. function MultiselectCheckbox({ options, onChange }) {
  12. const [data, setData] = React.useState(options);
  13. const toggle = item => {
  14. data.map((_, key) => {
  15. if (data[key].label === item.label) data[key].checked = !item.checked;
  16. });
  17. setData([...data]);
  18. onChange(data);
  19. };
  20. return (
  21. <ul style={style.listContainer}>
  22. {data.map(item => {
  23. return (
  24. <li key={item.label} style={style.itemStyle} onClick={() => toggle(item)}>
  25. <input readOnly type="checkbox" checked={item.checked || false} />
  26. {item.label}
  27. </li>
  28. );
  29. })}
  30. </ul>
  31. );
  32. }

例子

  1. export default function() {
  2. const options = [{ label: "Item One" }, { label: "Item Two" }];
  3. return (
  4. <MultiselectCheckbox
  5. options={options}
  6. onChange={data => {
  7. console.log(data);
  8. }}
  9. />
  10. );
  11. }

ps:

PasswordRevealer密码可见

使用“显示”按钮呈现密码输入字段。

  • 使用React.useState()钩子创建shown状态变量并将其值设置为false
  • 使用div>包装<input><button>元素,用于切换textpassword之间输入字段的类型。
  1. function PasswordRevealer({ value }) {
  2. const [shown, setShown] = React.useState(false);
  3. return (
  4. <div>
  5. <input type={shown ? 'text' : 'password'} value={value} onChange={() => {}} />
  6. <button onClick={() => setShown(!shown)}>显示/隐藏</button>
  7. </div>
  8. );
  9. }

例子

  1. export default function() {
  2. return <PasswordRevealer />;
  3. }

ps:

Select下拉选择器

呈现一个<select>元素,该元素使用回调函数将其值传递给父组件。

  • 使用对象解构来设置<select>元素的某些属性的默认值。
  • 使用适当的属性渲染一个<select>元素,并使用onChange事件中的callback函数将textarea的值传递给父元素。
  • values数组上使用destructuring来传递valuetext元素的数组以及selected属性来定义<select>元素的初始value
  1. function Select({
  2. values,
  3. callback,
  4. disabled = false,
  5. readonly = false,
  6. selected
  7. }) {
  8. const [current, setCurrent] = React.useState(selected);
  9. const handleChange = ({ target: { value } }) => {
  10. setCurrent(value);
  11. callback(value);
  12. };
  13. return (
  14. <select
  15. value={current}
  16. disabled={disabled}
  17. readOnly={readonly}
  18. onChange={handleChange}
  19. >
  20. {values.map(([value, text]) => (
  21. <option value={value} key={value}>
  22. {text}
  23. </option>
  24. ))}
  25. </select>
  26. );
  27. }

例子

  1. export default function() {
  2. let choices = [
  3. ["grapefruit", "Grapefruit"],
  4. ["lime", "Lime"],
  5. ["coconut", "Coconut"],
  6. ["mango", "Mango"]
  7. ];
  8. return (
  9. <Select
  10. values={choices}
  11. selected={"lime"}
  12. callback={val => {
  13. console.log(val);
  14. }}
  15. />
  16. );
  17. }

ps: 这里的实现跟官方不同,官方使用 option 的 selected 属性,但浏览器报错说不应使用,故更改为 select 的 value 属性。

Slider滑块元素

呈现滑块元素,使用回调函数将其值传递给父组件。

  • 使用对象解构来设置<input>元素的某些属性的默认值。
  • 渲染一个类型为range<input>元素和相应的属性,使用onChange事件中的callback函数将输入值传递给父元素。
  1. function Slider({ callback, disabled = false, readOnly = false }) {
  2. return (
  3. <input
  4. type="range"
  5. disabled={disabled}
  6. readOnly={readOnly}
  7. onChange={({ target: { value } }) => callback(value)}
  8. />
  9. );
  10. }

例子

  1. export default function() {
  2. return <Slider callback={val => console.log(val)} />;
  3. }

ps:

TextArea多行文本

呈现一个<textarea>元素,该元素使用回调函数将其值传递给父组件。

  • 使用对象解构来设置<textarea>元素的某些属性的默认值。
  • 使用适当的属性渲染<textarea>元素,并使用onChange事件中的callback函数将textarea的值传递给父元素。
  1. function TextArea({
  2. callback,
  3. cols = 20,
  4. rows = 2,
  5. disabled = false,
  6. readOnly = false,
  7. placeholder = ''
  8. }) {
  9. return (
  10. <textarea
  11. cols={cols}
  12. rows={rows}
  13. disabled={disabled}
  14. readOnly={readOnly}
  15. placeholder={placeholder}
  16. onChange={({ target: { value } }) => callback(value)}
  17. />
  18. );
  19. }

例子

  1. export default function() {
  2. return (
  3. <TextArea
  4. placeholder="Insert some text here..."
  5. callback={val => console.log(val)}
  6. />
  7. );
  8. }

ps:

Object对象渲染

TreeView可折叠无限层级树组件

可折叠、无限层级、支持数组和对象的树组件。

  • 使用对象解构来设置某些传入属性的默认值。
  • 使用传入的 toggled 属性来确定内容的初始状态(折叠/展开)。
  • 使用React.setState() hook 来创建isToggled状态变量,并在最初为它赋予传入的 toggled的值。
  • 返回一个<div>来包装组件的内容和用于改变组件的isToggled状态的<span>元素。
  • 根据data上的isParentToggledisTogglednameArray.isArray()确定组件的外观。
  • 对于data中的每个子节点,确定它是对象还是数组,并递归渲染子树。
  • 否则,使用适当的样式渲染一个<p>元素。

样式:

  1. /* 树节点的基本样式 */
  2. .tree-element {
  3. margin: 0;
  4. position: relative;
  5. }
  6. div.tree-element:before {
  7. content: '';
  8. position: absolute;
  9. top: 24px;
  10. left: 1px;
  11. height: calc(100% - 48px);
  12. border-left: 1px solid gray;
  13. }
  14. /* 切换显示、隐藏的按钮元素 */
  15. .toggler {
  16. position: absolute;
  17. top: 10px;
  18. left: 0px;
  19. width: 0;
  20. height: 0;
  21. border-top: 4px solid transparent;
  22. border-bottom: 4px solid transparent;
  23. border-left: 5px solid gray;
  24. cursor: pointer;
  25. }
  26. .toggler.closed {
  27. transform: rotate(90deg);
  28. }
  29. /* 隐藏节点内容 */
  30. .collapsed {
  31. display: none;
  32. }

树组件:

  1. import styles from "./TreeView.css";
  2. function TreeView({
  3. // 使用对象解构来设置某些传入属性的默认值
  4. data,
  5. toggled = true, // 折叠按钮,是否处于折叠状态
  6. name = null, // 当前属性名,如果子元素是对象显示
  7. isLast = true, // 是否最后一个
  8. isChildElement = false, // 是否子元素
  9. isParentToggled = true // 是否被父节点折叠
  10. }) {
  11. const [isToggled, setIsToggled] = React.useState(toggled);
  12. return (
  13. <div
  14. style={{ marginLeft: isChildElement ? 16 : 4 + "px" }}
  15. // 如果父折叠就隐藏
  16. className={isParentToggled ? styles["tree-element"] : styles.collapsed}
  17. >
  18. {/* 折叠按钮,点击设置反状态 */}
  19. <span
  20. className={
  21. isToggled ? styles.toggler : `${styles.toggler} ${styles.closed}`
  22. }
  23. onClick={() => setIsToggled(!isToggled)}
  24. />
  25. {name ? <strong>&nbsp;&nbsp;{name}: </strong> : <span>&nbsp;&nbsp;</span>}
  26. {/* 开始符 */}
  27. {Array.isArray(data) ? "[" : "{"}
  28. {/* 子元素被折叠 */}
  29. {!isToggled && "..."}
  30. {/* 渲染对象的子元素 */}
  31. {Object.keys(data).map((v, i, a) =>
  32. // 是对象,递归调用自身
  33. typeof data[v] == "object" ? (
  34. <TreeView
  35. data={data[v]}
  36. key={i}
  37. isLast={i === a.length - 1}
  38. // 子元素的属性名,对象需要显示属性名,数组不显示
  39. name={Array.isArray(data) ? null : v}
  40. isChildElement
  41. isParentToggled={isParentToggled && isToggled}
  42. />
  43. ) : ( // 不是对象,显示内容即可
  44. <p
  45. key={i}
  46. style={{ marginLeft: 16 + "px" }}
  47. className={isToggled ? styles["tree-element"] : styles.collapsed}
  48. >
  49. {Array.isArray(data) ? "" : <strong>{v}: </strong>}
  50. {data[v]}
  51. {i === a.length - 1 ? "" : ","}
  52. </p>
  53. )
  54. )}
  55. {/* 结束符 */}
  56. {Array.isArray(data) ? "]" : "}"}
  57. {/* 不是最后元素,加个逗号 */}
  58. {!isLast ? "," : ""}
  59. </div>
  60. );
  61. }

例子

  1. export default function() {
  2. let data = {
  3. lorem: {
  4. ipsum: "dolor sit",
  5. amet: {
  6. consectetur: "adipiscing",
  7. elit: [
  8. "duis",
  9. "vitae",
  10. {
  11. semper: "orci"
  12. },
  13. {
  14. est: "sed ornare"
  15. },
  16. "etiam",
  17. ["laoreet", "tincidunt"],
  18. ["vestibulum", "ante"]
  19. ]
  20. },
  21. ipsum: "primis"
  22. }
  23. };
  24. return <TreeView data={data} name="data" />;
  25. }

ps:

String字符串处理

AutoLink自动识别文本中的链接

将字符串中的URL转换为适当的 <a> 元素。

  • 使用正则表达式配合 String.prototype.split()String.prototype.match()来查找字符串中的URL。
  • 返回一个<React.Fragment>不产生多余的元素;其匹配的URL呈现为<a>元素,必要时需要处理丢失的协议前缀补充为http://,并将其余字符串呈现为明文。
  1. import React from "react";
  2. function AutoLink({ text }) {
  3. // 用于找 url 的正则表达式
  4. const delimiter = /((?:https?:\/\/)?(?:(?:[a-z0-9]?(?:[a-z0-9\-]{1,61}[a-z0-9])?\.[^\.|\s])+[a-z\.]*[a-z]+|(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3})(?::\d{1,5})*[a-z0-9.,_\/~#&=;%+?\-\\(\\)]*)/gi;
  5. return (
  6. <React.Fragment>
  7. {/* 按正则分割为数组,最终渲染时被显示在了一起 */}
  8. {text.split(delimiter).map(word => {
  9. // foo bar baz
  10. // http://example.org
  11. // bar
  12. console.log(word);
  13. // 从以上分割的内容中找到具体的url的部分
  14. // 进行超链接的处理
  15. let match = word.match(delimiter);
  16. if (match) {
  17. let url = match[0];
  18. return (
  19. <a href={url.startsWith("http") ? url : `http://${url}`} key={url} target="_blank">{url}</a>
  20. );
  21. }
  22. return word;
  23. })}
  24. </React.Fragment>
  25. );
  26. }

例子

  1. export default function() {
  2. return <AutoLink text="foo bar baz http://example.org barhttp://baidu.com 123" />;
  3. }

Visual视觉效果渲染

Accordion手风琴组件

手风琴效果组件,包含多个可折叠内容。

  • 定义一个AccordionItem组件,将它传递给Accordion。并通过在props.children中识别函数的名称来删除AccordionItem所需的不必要的节点。
  • 每个AccordionItem组有一个<button>,用于通过props.handleClick回调更新Accordion和组件的内容,通过props.children向下传递,它的折叠状态由props.isCollapsed确定。
  • Accordion组件中,使用React.useState()钩子将bindIndex状态变量的值初始化为props.defaultIndex
  • 在收集的节点上使用Array.prototype.map来渲染单个可折叠的元素。
  • 定义changeItem,它将在单击AccordionItem<button>时执行。 changeItem执行传递的回调,onItemClick并根据点击的元素更新bindIndex

AccordionItem 组件:

  1. import React from "react";
  2. function AccordionItem(props) {
  3. const style = {
  4. collapsed: {
  5. display: "none"
  6. },
  7. expanded: {
  8. display: "block"
  9. },
  10. buttonStyle: {
  11. display: "block",
  12. width: "100%"
  13. }
  14. };
  15. return (
  16. <div>
  17. {/* 按钮,点击传入的 handleClick */}
  18. <button style={style.buttonStyle} onClick={() => props.handleClick()}>
  19. {props.label}
  20. </button>
  21. {/* 控制显示、隐藏状态 */}
  22. <div
  23. className="collapse-content"
  24. style={props.isCollapsed ? style.collapsed : style.expanded}
  25. aria-expanded={props.isCollapsed}
  26. >
  27. {/* 内容 */}
  28. {props.children}
  29. </div>
  30. </div>
  31. );
  32. }

Accordion 组件:

  1. function Accordion(props) {
  2. // 目前显示的 index
  3. const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
  4. // 点击即把 bindIndex 设置为自己
  5. const changeItem = itemIndex => {
  6. if (typeof props.onItemClick === "function") props.onItemClick(itemIndex);
  7. if (itemIndex !== bindIndex) setBindIndex(itemIndex);
  8. };
  9. // 筛选出传入的 AccordionItem 组件,忽略其他
  10. // item.type 对应 react 组件自身的函数, 函数.name 是函数名
  11. // 在本地环境,没有压缩,函数名就是组件名 AccordionItem
  12. // 线上压缩后,函数名没改为了单个字母,自然就无法判断了
  13. // 解决方案:跟它本身 .name 比较即可
  14. const items = props.children.filter(
  15. item => item.type.name === AccordionItem.name
  16. );
  17. return (
  18. <div className="wrapper">
  19. {items.map(({ props }) => (
  20. <AccordionItem
  21. isCollapsed={bindIndex === props.index}
  22. label={props.label}
  23. handleClick={() => changeItem(props.index)}
  24. children={props.children}
  25. />
  26. ))}
  27. </div>
  28. );
  29. }

例子

  1. export default function() {
  2. return (
  3. <Accordion defaultIndex="1" onItemClick={console.log}>
  4. <AccordionItem label="A" index="1">
  5. Lorem ipsum
  6. </AccordionItem>
  7. <AccordionItem label="B" index="2">
  8. Dolor sit amet
  9. </AccordionItem>
  10. </Accordion>
  11. );
  12. }

Carousel轮播组件

轮播组件。

  • 使用React.setState() hook 来创建active状态变量,并给它一个值’0’(第一项的索引)。
  • 使用style对象来保存各个组件的样式。
  • 使用React.setEffect() hook 使用setTimeoutactive的值更新为下一个项的索引。
  • 构造props,计算是否应将可见性样式设置为“可见”或不对每个轮播项目进行映射,并相应地将组合样式应用于轮播项目组件。
  • 使用React.cloneElement()渲染轮播项目,并将其余的props与计算出的样式一起传递下来。
  1. function Carousel(props) {
  2. // active 当前轮播激活的索引
  3. const [active, setActive] = React.useState(0);
  4. const style = {
  5. carousel: {
  6. position: "relative"
  7. },
  8. carouselItem: {
  9. position: "absolute",
  10. visibility: "hidden"
  11. },
  12. visible: {
  13. visibility: "visible"
  14. }
  15. };
  16. React.useEffect(() => {
  17. // 将 active 的值更新为下一个项的索引
  18. setTimeout(() => {
  19. const { carouselItems } = props;
  20. // 因为 active 在 render 中使用了, active 改变会影响视图而重新渲染,所以也会再次触发 useEffect
  21. setActive((active + 1) % carouselItems.length);
  22. }, 1000);
  23. });
  24. const { carouselItems, ...rest } = props;
  25. return (
  26. <div style={style.carousel}>
  27. {carouselItems.map((item, index) => {
  28. // 激活就显示,否则隐藏
  29. const activeStyle = active === index ? style.visible : {};
  30. // 克隆出一个组件来渲染
  31. return React.cloneElement(item, {
  32. ...rest,
  33. style: {
  34. ...style.carouselItem,
  35. ...activeStyle
  36. },
  37. key: index
  38. });
  39. })}
  40. </div>
  41. );
  42. }

例子

  1. export default function() {
  2. return (
  3. <Carousel
  4. carouselItems={[
  5. <div>carousel item 1</div>,
  6. <div>carousel item 2</div>,
  7. <div>carousel item 3</div>
  8. ]}
  9. />
  10. );
  11. }

Collapse折叠面板

折叠面板组件。

  • 使用React.setState() hook 创建isCollapsed状态变量,初始值为props.collapsed
  • 使用一个对象style来保存单个组件及其状态的样式。
  • 使用<div>来包装改变组件的isCollapsed状态的<button>和组件的内容,通过props.children传递。
  • 根据isCollapsed确定内容的外观,并从style对象应用适当的CSS规则。
  • 最后,根据isCollapsed更新aria-expanded属性的值。
  1. function Collapse(props) {
  2. const [isCollapsed, setIsCollapsed] = React.useState(props.collapsed);
  3. const style = {
  4. collapsed: {
  5. display: "none"
  6. },
  7. expanded: {
  8. display: "block"
  9. },
  10. buttonStyle: {
  11. display: "block",
  12. width: "100%"
  13. }
  14. };
  15. return (
  16. <div>
  17. <button
  18. style={style.buttonStyle}
  19. onClick={() => setIsCollapsed(!isCollapsed)}
  20. >
  21. {isCollapsed ? "显示" : "隐藏"} 内容
  22. </button>
  23. <div
  24. className="collapse-content"
  25. // 决定显示和折叠
  26. style={isCollapsed ? style.collapsed : style.expanded}
  27. // aria-expanded 是给 Screen Reader 用来 判断当前元素状态的辅助属性
  28. aria-expanded={isCollapsed}
  29. >
  30. {props.children}
  31. </div>
  32. </div>
  33. );
  34. }

例子

  1. export default function() {
  2. return (
  3. <Collapse>
  4. <h1>This is a collapse</h1>
  5. <p>Hello world!</p>
  6. </Collapse>
  7. );
  8. }

CountDown倒计时

渲染倒数计时器,在达到零时打印消息。

  • 使用对象解构来设置hoursminutessecondsprop 的默认值。
  • 使用React.useState()钩子来创建timepausedover状态变量,并将它们的值分别设置为传递的props,falsefalse的值。
  • 创建一个方法tick,它根据当前值更新time的值(即将时间减少一秒)。
  • 如果pausedovertruetick将立即返回。
  • 创建一个方法reset,将所有状态变量重置为其初始状态。
  • 使用React.useEffect()钩子通过使用setInterval()每秒调用tick方法,并在卸载组件时使用clearInterval()进行清理。
  • 使用<div>time状态变量的文本表示形式包装<p>元素,以及分别暂停/取消暂停和重启计时器的两个<button>元素。
  • 如果overtrue,计时器将显示一条消息,而不是 time 的值。
  1. import React from "react";
  2. function CountDown({ hours = 0, minutes = 0, seconds = 0 }) {
  3. const [paused, setPaused] = React.useState(false);
  4. const [over, setOver] = React.useState(false);
  5. // time 默认值是一个 object
  6. const [time, setTime] = React.useState({
  7. hours: parseInt(hours),
  8. minutes: parseInt(minutes),
  9. seconds: parseInt(seconds)
  10. });
  11. const tick = () => {
  12. // 暂停,或已结束
  13. if (paused || over) return;
  14. if (time.hours === 0 && time.minutes === 0 && time.seconds === 0)
  15. setOver(true);
  16. else if (time.minutes === 0 && time.seconds === 0)
  17. setTime({
  18. hours: time.hours - 1,
  19. minutes: 59,
  20. seconds: 59
  21. });
  22. else if (time.seconds === 0)
  23. setTime({
  24. hours: time.hours,
  25. minutes: time.minutes - 1,
  26. seconds: 59
  27. });
  28. else
  29. setTime({
  30. hours: time.hours,
  31. minutes: time.minutes,
  32. seconds: time.seconds - 1
  33. });
  34. };
  35. // 重置
  36. const reset = () => {
  37. setTime({
  38. hours: parseInt(hours),
  39. minutes: parseInt(minutes),
  40. seconds: parseInt(seconds)
  41. });
  42. setPaused(false);
  43. setOver(false);
  44. };
  45. React.useEffect(() => {
  46. // 执行定时
  47. let timerID = setInterval(() => tick(), 1000);
  48. // 卸载组件时进行清理
  49. return () => clearInterval(timerID);
  50. });
  51. return (
  52. <div>
  53. <p>{`${time.hours
  54. .toString()
  55. .padStart(2, "0")}:${time.minutes
  56. .toString()
  57. .padStart(2, "0")}:${time.seconds.toString().padStart(2, "0")}`}</p>
  58. <div>{over ? "Time's up!" : ""}</div>
  59. <button onClick={() => setPaused(!paused)}>
  60. {paused ? "Resume" : "Pause"}
  61. </button>
  62. <button onClick={() => reset()}>Restart</button>
  63. </div>
  64. );
  65. }

例子

  1. export default function() {
  2. return <CountDown hours="1" minutes="45" />;
  3. }

FileDrop文件拖放组件

文件拖放组件。

  • 为此组件创建一个名为dropRef的引用。
  • 使用React.useState()钩子来创建dragfilename变量,分别初始化为false和空字符串。变量dragCounterdrag用于确定是否正在拖动文件,而filename用于存储被删除文件的名称。
  • 创建handleDraghandleDragInhandleDragOuthandleDrop方法来处理拖放功能,将它们绑定到组件的上下文。
  • 每个方法都将处理一个特定的事件,在React.useEffect()钩子及其附加的cleanup()方法中创建和删除它的监听器。
  • handleDrag阻止浏览器打开拖动的文件,handleDragInhandleDragOut处理进入和退出组件的拖动文件,而handleDrop处理被删除的文件并将其传递给props.handleDrop
  • 返回一个适当样式的<div>并使用dragfilename来确定其内容和样式。
  • 最后,将创建的<div>ref绑定到dropRef
  1. .filedrop {
  2. min-height: 120px;
  3. border: 3px solid #d3d3d3;
  4. text-align: center;
  5. font-size: 24px;
  6. padding: 32px;
  7. border-radius: 4px;
  8. }
  9. .filedrop.drag {
  10. border: 3px dashed #1e90ff;
  11. }
  12. .filedrop.ready {
  13. border: 3px solid #32cd32;
  14. }
  1. import React from "react";
  2. import styles from "./FileDrop.css";
  3. function FileDrop(props) {
  4. const [drag, setDrag] = React.useState(false);
  5. const [filename, setFilename] = React.useState("");
  6. // 创建组件引用
  7. let dropRef = React.createRef();
  8. let dragCounter = 0;
  9. const handleDrag = e => {
  10. e.preventDefault();
  11. e.stopPropagation();
  12. };
  13. const handleDragIn = e => {
  14. e.preventDefault();
  15. e.stopPropagation();
  16. dragCounter++;
  17. if (e.dataTransfer.items && e.dataTransfer.items.length > 0) setDrag(true);
  18. };
  19. const handleDragOut = e => {
  20. e.preventDefault();
  21. e.stopPropagation();
  22. dragCounter--;
  23. if (dragCounter === 0) setDrag(false);
  24. };
  25. const handleDrop = e => {
  26. e.preventDefault();
  27. e.stopPropagation();
  28. setDrag(false);
  29. if (e.dataTransfer.files && e.dataTransfer.files.length > 0) {
  30. props.handleDrop(e.dataTransfer.files[0]);
  31. setFilename(e.dataTransfer.files[0].name);
  32. e.dataTransfer.clearData();
  33. dragCounter = 0;
  34. }
  35. };
  36. React.useEffect(() => {
  37. // 监听拖放事件
  38. let div = dropRef.current;
  39. div.addEventListener("dragenter", handleDragIn);
  40. div.addEventListener("dragleave", handleDragOut);
  41. div.addEventListener("dragover", handleDrag);
  42. div.addEventListener("drop", handleDrop);
  43. // 销毁时移除事件
  44. return function cleanup() {
  45. div.removeEventListener("dragenter", handleDragIn);
  46. div.removeEventListener("dragleave", handleDragOut);
  47. div.removeEventListener("dragover", handleDrag);
  48. div.removeEventListener("drop", handleDrop);
  49. };
  50. });
  51. return (
  52. <div
  53. // ref 引用
  54. ref={dropRef}
  55. className={
  56. drag
  57. ? `${styles.filedrop} ${styles.drag}`
  58. : filename
  59. ? `${styles.filedrop} ${styles.ready}`
  60. : styles.filedrop
  61. }
  62. >
  63. {filename && !drag ? <div>{filename}</div> : <div>Drop files here!</div>}
  64. </div>
  65. );
  66. }

例子

  1. export default function() {
  2. return <FileDrop handleDrop={console.log} />;
  3. }

Mailto发送电子邮件

格式化为发送电子邮件的链接。

  • 构造组件的props,使用emailsubjectbody创建一个具有href属性的<a>元素。
  • 使用props.children呈现链接的内容。
  1. function Mailto({ email, subject, body, ...props }) {
  2. return (
  3. <a href={`mailto:${email}?subject=${subject || ''}&body=${body || ''}`}>{props.children}</a>
  4. );
  5. }

例子

  1. export default function() {
  2. return (
  3. <Mailto email="foo@bar.baz" subject="Hello" body="Hello world!">
  4. Mail me!
  5. </Mailto>
  6. );
  7. }

Modal模态框组件

可通过事件控制的模态组件。

要使用该组件,只导入一次Modal,然后通过将一个布尔值传递给isVisible属性来显示它。

  • 使用对象解构来设置模态组件的某些属性的默认值。
  • 定义keydownHandler方法,用于处理所有键盘事件,可以根据你的需要使用它来调度动作(例如,当按下Esc时关闭模态)。 使用React.useEffect()hook来添加或删除keydown事件监听器,它调用keydownHandler使用isVisible道具来确定是否应该显示模态。 *使用CSS来设置和定位模态组件。

样式:

  1. .modal {
  2. position: fixed;
  3. top: 0;
  4. bottom: 0;
  5. left: 0;
  6. right:0;
  7. width: 100%;
  8. z-index: 9999;
  9. display: flex;
  10. align-items: center;
  11. justify-content: center;
  12. background-color: rgba(0, 0, 0, 0.25);
  13. animation-name: appear;
  14. animation-duration: 300ms;
  15. }
  16. .modal-dialog{
  17. width: 100%;
  18. max-width: 550px;
  19. background: white;
  20. position: relative;
  21. margin: 0 20px;
  22. max-height: calc(100vh - 40px);
  23. text-align: left;
  24. display: flex;
  25. flex-direction: column;
  26. overflow:hidden;
  27. box-shadow: 0 4px 8px 0 rgba(0,0,0,0.2),0 6px 20px 0 rgba(0,0,0,0.19);
  28. -webkit-animation-name: animatetop;
  29. -webkit-animation-duration: 0.4s;
  30. animation-name: slide-in;
  31. animation-duration: 0.5s;
  32. }
  33. .modal-header,.modal-footer{
  34. display: flex;
  35. align-items: center;
  36. padding: 1rem;
  37. }
  38. .modal-header{
  39. border-bottom: 1px solid #dbdbdb;
  40. justify-content: space-between;
  41. }
  42. .modal-footer{
  43. border-top: 1px solid #dbdbdb;
  44. justify-content: flex-end;
  45. }
  46. .modal-close{
  47. cursor: pointer;
  48. padding: 1rem;
  49. margin: -1rem -1rem -1rem auto;
  50. }
  51. .modal-body{
  52. overflow: auto;
  53. }
  54. .modal-content{
  55. padding: 1rem;
  56. }
  57. @keyframes appear {
  58. from {opacity: 0;}
  59. to {opacity: 1;}
  60. }
  61. @keyframes slide-in {
  62. from {transform: translateY(-150px);}
  63. to { transform: translateY(0);}
  64. }

组件:

  1. import React from "react";
  2. import styles from "./Modal.css";
  3. function Modal({ isVisible = false, title, content, footer, onClose }) {
  4. React.useEffect(() => {
  5. // 监听事件
  6. document.addEventListener("keydown", keydownHandler);
  7. // 取消监听
  8. return () => document.removeEventListener("keydown", keydownHandler);
  9. });
  10. function keydownHandler({ key }) {
  11. // esc 键,关闭模态框
  12. switch (key) {
  13. case "Escape":
  14. onClose();
  15. break;
  16. default:
  17. }
  18. }
  19. // 控制模态框显示
  20. return !isVisible ? null : (
  21. <div className={styles["modal"]} onClick={onClose}>
  22. <div
  23. className={styles["modal-dialog"]}
  24. onClick={e => e.stopPropagation()}
  25. >
  26. <div className={styles["modal-header"]}>
  27. <h3 className={styles["modal-title"]}>{title}</h3>
  28. <span className={styles["modal-close"]} onClick={onClose}>
  29. &times;
  30. </span>
  31. </div>
  32. <div className={styles["modal-body"]}>
  33. <div className={styles["modal-content"]}>{content}</div>
  34. </div>
  35. {footer && <div className={styles["modal-footer"]}>{footer}</div>}
  36. </div>
  37. </div>
  38. );
  39. }

例子

  1. // 将组件添加到 render 函数
  2. function App() {
  3. const [isModal, setModal] = React.useState(false);
  4. return (
  5. <React.Fragment>
  6. {/* 按钮显示模态框 */}
  7. <button onClick={() => setModal(true)}>显示模态框</button>
  8. <Modal
  9. isVisible={isModal}
  10. title="标题"
  11. content={<p>正文</p>}
  12. footer={<button onClick={() => setModal(false)}>关闭模态框</button>}
  13. onClose={() => setModal(false)}
  14. />
  15. </React.Fragment>
  16. );
  17. }
  18. export default function() {
  19. return <App />;
  20. }

StarRating星级评分

星级评分组件。

  • 定义一个名为“Star”的组件,它将根据父组件的状态为每个星形呈现适当的外观。
  • StarRating组件中,使用React.useState()钩子来定义ratingselection状态变量,初始值为props.rating(如果无效或未传入,则为 0 )和 0 。
  • 创建一个方法hoverOver,根据传入的event更新selectedrating
  • 创建一个<div>来包装<Star>组件,这些组件是使用Array.prototype.map在5个元素的数组上创建的,使用Array.from创建,并处理onMouseLeaveselection设置为0的事件,onClick事件设置ratingonMouseOver事件,分别将selection设置为event.targetstar-id属性。
  • 最后,将适当的值传递给每个<Star>组件(starIdmarked)。

星星组件:

  1. function Star({ marked, starId }) {
  2. return (
  3. <span star-id={starId} style={{ color: "#ff9933" }} role="button">
  4. {/* 空星,实星 */}
  5. {marked ? "\u2605" : "\u2606"}
  6. </span>
  7. );
  8. }

星级评分:

  1. function StarRating(props) {
  2. // 分数显示
  3. const [rating, setRating] = React.useState(
  4. typeof props.rating == "number" ? props.rating : 0
  5. );
  6. // 鼠标移入效果
  7. const [selection, setSelection] = React.useState(0);
  8. const hoverOver = event => {
  9. let val = 0;
  10. if (event && event.target && event.target.getAttribute("star-id"))
  11. val = event.target.getAttribute("star-id");
  12. setSelection(val);
  13. };
  14. return (
  15. <div
  16. // 鼠标移入效果
  17. onMouseOut={() => hoverOver(null)}
  18. // 点击选中分数
  19. onClick={event =>
  20. setRating(event.target.getAttribute("star-id") || rating)
  21. }
  22. onMouseOver={hoverOver}
  23. >
  24. {/* 创建5个组件 */}
  25. {Array.from({ length: 5 }, (v, i) => (
  26. <Star
  27. starId={i + 1}
  28. key={`star_${i + 1} `}
  29. marked={selection ? selection >= i + 1 : rating >= i + 1}
  30. />
  31. ))}
  32. </div>
  33. );
  34. }

例子

  1. export default function() {
  2. return <div>
  3. <StarRating />
  4. <StarRating rating={2} />
  5. </div>;
  6. }

Tabs选项卡组件

选项卡组件。

  • 定义一个TabItem组件,将它传递给Tab并通过在props.children中识别函数的名称来删除除了TabItem外的不必要的节点。
  • 使用React.useState() hook 将bindIndex状态变量的值初始化为props.defaultIndex
  • 使用Array.prototype.map来渲染tab-menutab-view
  • 定义 changeTab ,用于 tab-menu 单击 <button> 时执行。
  • 这导致根据他们的index 反过来重新渲染 tab-view项的styleclassName以及tab-menu
  • changeTab 执行传递的回调函数 onTabClick ,并更新 bindIndex ,这会导致重新渲染,根据它们的 index 改变 tab-view 项目和 tab-menu 按钮的 styleclassName
  1. .tab-menu > button {
  2. cursor: pointer;
  3. padding: 8px 16px;
  4. border: 0;
  5. border-bottom: 2px solid transparent;
  6. background: none;
  7. }
  8. .tab-menu > button.focus {
  9. border-bottom: 2px solid #007bef;
  10. }
  11. .tab-menu > button:hover {
  12. border-bottom: 2px solid #007bef;
  13. }
  1. import styles from "./Tabs.css";
  2. function TabItem(props) {
  3. return <div {...props} />;
  4. }
  5. function Tabs(props) {
  6. const [bindIndex, setBindIndex] = React.useState(props.defaultIndex);
  7. const changeTab = newIndex => {
  8. if (typeof props.onTabClick === "function") props.onTabClick(newIndex);
  9. setBindIndex(newIndex);
  10. };
  11. const items = props.children.filter(item => item.type.name === TabItem.name);
  12. return (
  13. <div className={styles["wrapper"]}>
  14. <div className={styles["tab-menu"]}>
  15. {items.map(({ props: { index, label } }) => (
  16. <button
  17. onClick={() => changeTab(index)}
  18. key={index}
  19. className={bindIndex === index ? styles["focus"] : ""}
  20. >
  21. {label}
  22. </button>
  23. ))}
  24. </div>
  25. <div className={styles["tab-view"]}>
  26. {items.map(({ props }) => (
  27. <div
  28. {...props}
  29. className={styles["tab-view_item"]}
  30. key={props.index}
  31. style={{ display: bindIndex === props.index ? "block" : "none" }}
  32. />
  33. ))}
  34. </div>
  35. </div>
  36. );
  37. }

例子

  1. export default function() {
  2. return (
  3. <Tabs defaultIndex="1" onTabClick={console.log}>
  4. <TabItem label="A" index="1">
  5. A 选修卡的内容
  6. </TabItem>
  7. <TabItem label="B" index="2">
  8. B 选修卡的内容
  9. </TabItem>
  10. </Tabs>
  11. );
  12. }

Ticker时间控制组件

时间控制组件

  • 使用React.useState() hook 将ticker状态变量初始化为0
  • 定义两个方法,tickreset,它们将根据interval周期性地递增timer并分别重置interval
  • 返回带有两个<button>元素的<div>,分别调用tickreset
  1. function Ticker(props) {
  2. // 当前 ticker ,默认 0
  3. const [ticker, setTicker] = React.useState(0);
  4. let interval = null;
  5. // 开始计时
  6. const tick = () => {
  7. reset();
  8. interval = setInterval(() => {
  9. if (ticker < props.times) setTicker(ticker + 1);
  10. else clearInterval(interval);
  11. }, props.interval);
  12. };
  13. // 重置为0,并清除计时器
  14. const reset = () => {
  15. setTicker(0);
  16. clearInterval(interval);
  17. };
  18. return (
  19. <div>
  20. <span style={{ fontSize: 100 }}>{ticker}</span>
  21. <button onClick={tick}>Tick!</button>
  22. <button onClick={reset}>Reset</button>
  23. </div>
  24. );
  25. }

注:useState 与 setInterval 运行在 umi 的 demo 中存在异常,这里使用“类组件”的形式完成Ticker组件。

  1. import React from "react";
  2. class Ticker extends React.Component {
  3. constructor(props) {
  4. super(props);
  5. this.state = { ticker: 0 };
  6. this.interval = null;
  7. }
  8. tick = () => {
  9. this.reset();
  10. this.interval = setInterval(() => {
  11. if (this.state.ticker < this.props.times) {
  12. this.setState(({ ticker }) => ({ ticker: ticker + 1 }));
  13. } else {
  14. clearInterval(this.interval);
  15. }
  16. }, this.props.interval);
  17. };
  18. reset = () => {
  19. this.setState({ ticker: 0 });
  20. clearInterval(this.interval);
  21. };
  22. render() {
  23. return (
  24. <div>
  25. <span style={{ fontSize: 100 }}>{this.state.ticker}</span>
  26. <button onClick={this.tick}>Tick!</button>
  27. <button onClick={this.reset}>Reset</button>
  28. </div>
  29. );
  30. }
  31. }

例子

  1. export default function() {
  2. return <Ticker times={5} interval={1000} />;
  3. }

Toggle开关组件

开关组件

  • 使用React.useState()isToggleOn状态变量初始化为false
  • 使用一个对象style来保存单个组件及其状态的样式。
  • 返回一个<button>,当它的onClick事件被触发时改变组件的isToggledOn,并根据isToggleOn确定内容的外观,从style对象应用适当的CSS规则。
  1. function Toggle(props) {
  2. const [isToggleOn, setIsToggleOn] = React.useState(false);
  3. style = {
  4. on: {
  5. backgroundColor: 'green'
  6. },
  7. off: {
  8. backgroundColor: 'grey'
  9. }
  10. };
  11. return (
  12. <button onClick={() => setIsToggleOn(!isToggleOn)} style={isToggleOn ? style.on : style.off}>
  13. {isToggleOn ? 'ON' : 'OFF'}
  14. </button>
  15. );
  16. }

例子

  1. export default function() {
  2. return <Toggle />;
  3. }

Tooltip提示

提示组件。

  • 使用React.useState()钩子创建show变量并将其初始化为false
  • 返回一个<div>元素,其中包含将作为工具提示的<div>和传递给组件的children
  • 通过改变show变量的值来处理onMouseEnteronMouseLeave方法。
  1. .tooltip {
  2. position: relative;
  3. background: rgba(0, 0, 0, 0.7);
  4. color: white;
  5. visibility: hidden;
  6. padding: 5px;
  7. border-radius: 5px;
  8. }
  9. .tooltip-arrow {
  10. position: absolute;
  11. top: 100%;
  12. left: 50%;
  13. border-width: 5px;
  14. border-style: solid;
  15. border-color: rgba(0, 0, 0, 0.7) transparent transparent;
  16. }
  1. import styles from "./Tooltip.css";
  2. function Tooltip({ children, text, ...rest }) {
  3. const [show, setShow] = React.useState(false);
  4. return (
  5. <div>
  6. <div
  7. className={styles["tooltip"]}
  8. style={show ? { visibility: "visible" } : {}}
  9. >
  10. {text}
  11. <span className={styles["tooltip-arrow"]} />
  12. </div>
  13. <div
  14. {...rest}
  15. onMouseEnter={() => setShow(true)}
  16. onMouseLeave={() => setShow(false)}
  17. >
  18. {children}
  19. </div>
  20. </div>
  21. );
  22. }

例子

  1. export default function() {
  2. return (
  3. <Tooltip text="提示文本~">
  4. <button>鼠标移入!</button>
  5. </Tooltip>
  6. );
  7. }

注:本仓库使用“谷歌机器翻译+本人校对优化”的方式进行中文化。


30-seconds-of-react正在进行中。 如果您想贡献,请查看未解决的问题,看看您可以在何处以及如何提供帮助!

原版README是使用 markdown-builder 构建的。