Skip to content

React 组件必须以大写字母开头

显示数据

js
const user = {
  name: 'Hedy Lamarr',
  imageUrl: 'https://i.imgur.com/yXOvdOSs.jpg',
  imageSize: 90,
};

export default function Profile() {
  return (
    <>
      <h1>{user.name}</h1>
      <img
        className="avatar"
        src={user.imageUrl}
        alt={'Photo of ' + user.name}
        style={{
          width: user.imageSize,
          height: user.imageSize
        }}
      />
    </>
  );
}

style=&#123;&#123;&#125;&#125; 并不是一个特殊的语法,而是 style={} JSX 大括号内的一个普通 {} 对象

##条件渲染

js
<div>
  {isLoggedIn ? (
    <AdminPanel />
  ) : (
    <LoginForm />
  )}
</div>

##渲染列表

js
const products = [
  { title: 'Cabbage', id: 1 },
  { title: 'Garlic', id: 2 },
  { title: 'Apple', id: 3 },
];
const listItems = products.map(product =>
  <li key={product.id}>
    {product.title}
  </li>
);

return (
  <ul>{listItems}</ul>
);

为每个列表项显示多个 DOM 节点

如果你想让每个列表项都输出多个 DOM 节点而非一个的话,该怎么做呢?

Fragment 语法的简写形式 &lt;&gt; &lt;/&gt; 无法接受 key 值,所以你只能要么把生成的节点用一个 &lt;div&gt; 标签包裹起来,要么使用长一点但更明确的 &lt;Fragment&gt; 写法:

Fragment相当于vue语法的Template

jsx
import { Fragment } from 'react';

// ...

const listItems = people.map(person =>
  <Fragment key={person.id}>
    <h1>{person.name}</h1>
    <p>{person.bio}</p>
  </Fragment>
);

响应事件

js
function MyButton() {
  function handleClick() {
    alert('You clicked me!');
  }

  return (
    <button onClick={handleClick}>
      点我
    </button>
  );
}

或者,你也可以在 JSX 中定义一个内联的事件处理函数:

jsx
<button onClick={function handleClick() {
  alert('你点击了我!');
}}>

或者,直接使用更为简洁箭头函数:

jsx
<button onClick={() => {
  alert('你点击了我!');
}}>

注意,onClick={handleClick} 的结尾没有小括号!不要 调用 事件处理函数:你只需 把函数传递给事件 即可。当用户点击按钮时 React 会调用你传递的事件处理函数

State用法

js
import { useState } from 'react';

export default function MyApp() {
  return (
    <div>
      <h1>独立更新的计数器</h1>
      <MyButton />
      <MyButton />
    </div>
  );
}

function MyButton() {
	//index 是一个 state 变量,setIndex 是对应的 setter 函数
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <button onClick={handleClick}>
      点了 {count} 次
    </button>
  );
}

你将从 useState 中获得两样东西:当前的 state(count),以及用于更新它的函数(setCount)。你可以给它们起任何名字,但按照惯例会像 [something, setSomething] 这样为它们命名。

第一次显示按钮时,count 的值为 0,因为你把 0 传给了 useState()。当你想改变 state 时,调用 setCount() 并将新的值传递给它。点击该按钮计数器将递增:

在组件中使用 reducer

最后,你需要将 tasksReducer 导入到组件中。记得先从 React 中导入 useReducer Hook:

js
import { useReducer } from 'react';

接下来,你就可以替换掉之前的 useState:

js
const [tasks, setTasks] = useState(initialTasks);

只需要像下面这样使用 useReducer:

js
const [tasks, dispatch] = useReducer(tasksReducer, initialTasks);

useReducer 钩子接受 2 个参数:

1.一个 reducer 函数 2.一个初始的 state

prop用法

js
import { useState } from 'react';

export default function MyApp() {
  const [count, setCount] = useState(0);

  function handleClick() {
    setCount(count + 1);
  }

  return (
    <div>
      <h1>共同更新的计数器</h1>
      <MyButton count={count} onClick={handleClick} />
      <MyButton count={count} onClick={handleClick} />
    </div>
  );
}

function MyButton({ count, onClick }) {
  return (
    <button onClick={onClick}>
      点了 {count} 次
    </button>
  );
}

在声明 props 时, 不要忘记 ( 和 ) 之间的一对花括号 { 和 } :

jsx
function Avatar({ person, size }) {
  // ...
}

这种语法被称为 “解构”,等价于于从函数参数中读取属性:

jsx
function Avatar(props) {
  let person = props.person;
  let size = props.size;
  // ...
}

当你构建自己的组件时,你可以按你个人喜好命名事件处理函数的 prop。

jsx
function Button({ onSmash, children }) {
  return (
    <button onClick={onSmash}>
      {children}
    </button>
  );
}

export default function App() {
  return (
    <div>
      <Button onSmash={() => alert('正在播放!')}>
        播放电影
      </Button>
      <Button onSmash={() => alert('正在上传!')}>
        上传图片
      </Button>
    </div>
  );
}

阻止事件冒泡

如果你想阻止一个事件到达父组件,你需要像下面 Button 组件那样调用 e.stopPropagation() :

jsx
function Button({ onClick, children }) {
  return (
    <button onClick={e => {
      e.stopPropagation();
      onClick();
    }}>
      {children}
    </button>
  );
}

export default function Toolbar() {
  return (
    <div className="Toolbar" onClick={() => {
      alert('你点击了 toolbar !');
    }}>
      <Button onClick={() => alert('正在播放!')}>
        播放电影
      </Button>
      <Button onClick={() => alert('正在上传!')}>
        上传图片
      </Button>
    </div>
  );
}

阻止默认行为

相当于vue:v-on:click.prevent

jsx
export default function Signup() {
  return (
    <form onSubmit={e => {
      e.preventDefault();
      alert('提交表单!');
    }}>
      <input />
      <button>发送</button>
    </form>
  );
}

使用 JSX 展开语法传递 props

有时候,传递 props 会变得非常重复:

jsx
function Profile({ person, size, isSepia, thickBorder }) {
  return (
    <div className="card">
      <Avatar
        person={person}
        size={size}
        isSepia={isSepia}
        thickBorder={thickBorder}
      />
    </div>
  );
}

重复代码没有错(它可以更清晰)。但有时你可能会重视简洁。一些组件将它们所有的 props 转发给子组件,正如 Profile 转给 Avatar 那样。因为这些组件不直接使用他们本身的任何 props,所以使用更简洁的“展开”语法是有意义的:

jsx
function Profile(props) {
  return (
    <div className="card">
      <Avatar {...props} />
    </div>
  );
}

这会将 Profile 的所有 props 转发到 Avatar,而不列出每个名字。

当你将内容嵌套在 JSX 标签中时,父组件将在名为 children 的 prop 中接收到该内容。例如,下面的 Card 组件将接收一个被设为 &lt;Avatar /&gt; 的 children prop 并将其包裹在 div 中渲染:

jsx
import Avatar from './Avatar.js';

function Card({children }) {
  return (
    <div className="card">
      {children}
    </div>
  );
}

export default function Profile() {
  return (
    <Card>
      <Avatar
        size={100}
        person={{ 
          name: 'Katsuko Saruhashi',
          imageId: 'YfeOqp2'
        }}
      />
    </Card>
  );
}

定义组件

返回语句可以全写在一行上,如下面组件中所示:

js
return <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />;

但是,如果你的标签和 return 关键字不在同一行,则必须把它包裹在一对括号中,如下所示:(内部没有分号)

js
return (
  <div>
    <img src="https://i.imgur.com/MK3eW3As.jpg" alt="Katherine Johnson" />
  </div>
);

ref

js
import { useRef } from 'react';

export default function Form() {
  const inputRef = useRef(null);

  function handleClick() {
    inputRef.current.focus();
  }

  return (
    <>
      <input ref={inputRef} />
      <button onClick={handleClick}>
        聚焦输入框
      </button>
    </>
  );
}

组件的导入和导出

js
//App.js
import Gallery from './Gallery.js';

export default function App() {
  return (
    <Gallery />
  );
}
js
//Gallery.js
function Profile() {
  return (
    <img
      src="https://i.imgur.com/QIrZWGIs.jpg"
      alt="Alan L. Hart"
    />
  );
}

export default function Gallery() {
  return (
    <section>
      <h1>了不起的科学家们</h1>
      <Profile />
      <Profile />
      <Profile />
    </section>
  );
}

默认导出 vs 具名导出

语法导出语句导入语句
默认export default function Button() {}import Button from './Button.js';
具名export function Button() {}import { Button } from './Button.js';

同一文件中,有且仅有一个默认导出,但可以有多个具名导出!

JSX 规则

1. 只能返回一个根元素

如果你不想在标签中增加一个额外的 &lt;div&gt;,可以用 &lt;&gt;&lt;/&gt; 元素来代替:

2. 标签必须闭合

3.使用驼峰式命名法给大部分属性命名!

由于 class 是一个保留字,所以在 React 中需要用 className 来代替

使用大括号:一扇进入 JavaScript 世界的窗户

jsx
export default function TodoList() {
  const name = 'Gregorio Y. Zara';
  return (
    <h1>{name}的待办事项列表</h1>
  );
}

内联 style 属性 使用驼峰命名法编写。例如,HTML &lt;ul style="background-color: black"&gt; 在你的组件里应该写成 &lt;ul style=&#123;&#123; backgroundColor: 'black' &#125;&#125;&gt;

在这个示例中,person JavaScript 对象包含 name 中存储的字符串和 theme 对象:

jsx
const person = {
  name: 'Gregorio Y. Zara',
  theme: {
    backgroundColor: 'black',
    color: 'pink'
  }
};

该组件可以这样使用来自 person 的值:

jsx
<div style={person.theme}>
  <h1>{person.name}'s Todos</h1>

条件渲染

与运算符(&&)

你会遇到的另一个常见的快捷表达式是 JavaScript 逻辑与(&&)运算符。在 React 组件里,通常用在当条件成立时,你想渲染一些 JSX,或者不做任何渲染。使用 &&,你也可以实现仅当 isPacked 为 true 时,渲染勾选符号。

jsx
return (
  <li className="item">
    {name} {isPacked && '✅'}
  </li>
);

当 JavaScript && 表达式 的左侧(我们的条件)为 true 时,它则返回其右侧的值(在我们的例子里是勾选符号)。但条件的结果是 false,则整个表达式会变成 false。在 JSX 里,React 会将 false 视为一个“空值”,就像 null 或者 undefined,这样 React 就不会在这里进行任何渲染。