描述用户界面
React 是一个用于构建用户界面(UI)的 JavaScript 库,用户界面由按钮、文本和图像等小单元内容构建而成。React 帮助你把它们组合成可重用、可嵌套的 组件。从 web 端网站到移动端应用,屏幕上的所有内容都可以被分解成组件。在本章节中,你将学习如何创建、定制以及有条件地显示 React 组件。
In this chapter
你的第一个组件
React 应用是由被称为 组件 的独立 UI 片段构建而成。React 组件本质上是可以任意添加标记的 JavaScript 函数。组件可以小到一个按钮,也可以大到是整个页面。这是一个 Gallery
组件,用于渲染三个 Profile
组件:
function Profile() { return ( <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" /> ); } export default function Gallery() { return ( <section> <h1>Amazing scientists</h1> <Profile /> <Profile /> <Profile /> </section> ); }
import Profile from './Profile.js'; export default function Gallery() { return ( <section> <h1>Amazing scientists</h1> <Profile /> <Profile /> <Profile /> </section> ); }
使用 JSX 编写标记
每个 React 组件都是一个 JavaScript 函数,它可能包含一些标记,React 会将其渲染到浏览器中。React 组件使用一种叫做 JSX 的语法扩展来表示该标记。JSX 看起来很像 HTML,但它更为严格,可以显示动态信息。
如果我们把现有的 HTML 标记粘贴到 React 组件中,它并不一定能成功运行:
export default function TodoList() { return ( // This doesn't quite work! <h1>Hedy Lamarr's Todos</h1> <img src="https://i.imgur.com/yXOvdOSs.jpg" alt="Hedy Lamarr" class="photo" > <ul> <li>Invent new traffic lights <li>Rehearse a movie scene <li>Improve spectrum technology </ul>
如果你有像这样的现有的 HTML 片段,你可以使用它进行语法转换 converter:
export default function TodoList() { return ( <> <h1>Hedy Lamarr's Todos</h1> <img src="https://i.imgur.com/yXOvdOSs.jpg" alt="Hedy Lamarr" className="photo" /> <ul> <li>Invent new traffic lights</li> <li>Rehearse a movie scene</li> <li>Improve spectrum technology</li> </ul> </> ); }
在 JSX 中使用大括号编写 JavaScript
JSX 可以让你在 JavaScript 文件中编写类似 HTML 的标记语法,使渲染逻辑和内容展示维护在同一个地方。有时你会想在标记中添加一点 JavaScript 逻辑或引用一个动态属性。在这种情况下,你可以在 JSX 中使用花括号来为 JavaScript “开辟通道”:
const person = { name: 'Gregorio Y. Zara', theme: { backgroundColor: 'black', color: 'pink' } }; export default function TodoList() { return ( <div style={person.theme}> <h1>{person.name}'s Todos</h1> <img className="avatar" src="https://i.imgur.com/7vQD0fPs.jpg" alt="Gregorio Y. Zara" /> <ul> <li>Improve the videophone</li> <li>Prepare aeronautics lectures</li> <li>Work on the alcohol-fuelled engine</li> </ul> </div> ); }
将 props
传递给一个组件
React 组件使用 props 来进行组件之间的通讯。每个父组件都可以通过为子组件提供 props 的方式来传递信息。props 可能会让你想起 HTML 属性,但你可以通过它们传递任何 JavaScript 的值,包括对象、数组、函数、甚至是 JSX!
import { getImageUrl } from './utils.js' export default function Profile() { return ( <Card> <Avatar size={100} person={{ name: 'Katsuko Saruhashi', imageId: 'YfeOqp2' }} /> </Card> ); } function Avatar({ person, size }) { return ( <img className="avatar" src={getImageUrl(person)} alt={person.name} width={size} height={size} /> ); } function Card({ children }) { return ( <div className="card"> {children} </div> ); }
条件渲染
你的组件经常需要根据不同的条件来显示不同的东西。在 React 中,你可以使用 JavaScript 语法,如 if
语句、&&
和 ? :
操作符有条件地渲染 JSX。
在这个示例中,JavaScript 的 &&
操作符将会条件渲染一个复选标记:
function Item({ name, isPacked }) { return ( <li className="item"> {name} {isPacked && '✔'} </li> ); } export default function PackingList() { return ( <section> <h1>Sally Ride's Packing List</h1> <ul> <Item isPacked={true} name="Space suit" /> <Item isPacked={true} name="Helmet with a golden leaf" /> <Item isPacked={false} name="Photo of Tam" /> </ul> </section> ); }
渲染列表
通常,你需要根据数据集合来渲染多个较为类似的组件。你可以在 React 中使用 JavaScript 的 filter()
和 map()
来实现数组的过滤和转换,将数据数组转换为组件数组。
对于数组的每个元素项,你需要指定一个 key
。通常你需要使用数据库中的 ID 作为 key
。即使列表发生了变化,React 也可以通过 key 来跟踪每个元素在列表中的位置。
import { people } from './data.js'; import { getImageUrl } from './utils.js'; export default function List() { const listItems = people.map(person => <li key={person.id}> <img src={getImageUrl(person)} alt={person.name} /> <p> <b>{person.name}:</b> {' ' + person.profession + ' '} known for {person.accomplishment} </p> </li> ); return ( <article> <h1>Scientists</h1> <ul>{listItems}</ul> </article> ); }
保持组件的纯粹
有些 JavaScript 函数是 纯粹 的。纯函数的基本定义:
- 管好自己的事。 在函数调用前,它不会改变任何已经存在的对象或变量。
- 输入相同,输出也相同。 在输入相同的情况下,对纯函数来说应总是返回相同的结果。
严格遵循纯函数的定义编写组件,可以让代码库体量增长时,避免一些令人困惑的错误和不可预测的行为。下面是一个非纯函数组件的示例:
let guest = 0; function Cup() { // Bad: changing a preexisting variable! guest = guest + 1; return <h2>Tea cup for guest #{guest}</h2>; } export default function TeaSet() { return ( <> <Cup /> <Cup /> <Cup /> </> ); }
你可以通过传递一个 props
来使这个组件变得纯粹,而非修改已经存在的变量:
function Cup({ guest }) { return <h2>Tea cup for guest #{guest}</h2>; } export default function TeaSet() { return ( <> <Cup guest={1} /> <Cup guest={2} /> <Cup guest={3} /> </> ); }