React 是一个声明式,高效且灵活的用于构建用户界面的 JavaScript 库。

官网

JSX

React 中一般用 JSX 输出 UI。JSX 可以简单的理解为,包含 JavaScript 表达式的 HTML 。

  1. function formatName(user) {
  2. return user.firstName + ' ' + user.lastName;
  3. }
  4. const user = {
  5. firstName: 'Harper',
  6. lastName: 'Perez'
  7. };
  8. const element = (
  9. <h1>
  10. Hello, {formatName(user)}!
  11. </h1>
  12. );
  13. ReactDOM.render(
  14. element,
  15. document.getElementById('root')
  16. );

渲染列表

item 上一定要加 key。不要用数组的下标做为 Key。用数组的下标做为 Key 会有性能问题。

  1. <ul>
  2. {list.map(item =>
  3. (<li key={item.id>{item.name}</li>)
  4. }
  5. </ul>

条件渲染

  1. {isShow && <div>...</div>}
  2. <b>{isLoggedIn ? 'currently' : 'not'}</b>

一个组件返回多个元素

  1. render() {
  2. return (
  3. <React.Fragment>
  4. <ChildA />
  5. <ChildB />
  6. <ChildC />
  7. </React.Fragment>
  8. );
  9. }

简写

  1. render() {
  2. return (
  3. <>
  4. <ChildA />
  5. <ChildB />
  6. <ChildC />
  7. </>
  8. );
  9. }

事件

绑定事件

React 元素的事件处理和 DOM 元素的很相似,但是有一点语法上的不同:

  • React 事件的命名采用小驼峰式(camelCase),而不是纯小写。
  • 使用 JSX 语法时你需要传入一个函数作为事件处理函数,而不是一个字符串。
  1. <button onClick={activateLasers}>
  2. Activate Lasers
  3. </button>

回调中获取事件对象 & 传额外参数

  1. <button onClick={(e) => this.deleteRow(id, e)}>Delete Row</button>

停止冒泡到原生的事件

  1. <div onClick={e => e.nativeEvent.stopImmediatePropagation()}>...</div>

类组件

组件 API

setState(changeState, cb)

大部分情况是异步的。

生命周期

projects.wojtekmaj.pl_react-lifecycle-methods-diagram_.png

常用的生命周期如下:

render()

返回要渲染的元素。

constructor

主要用来设置 state 的初始值。

  1. constructor(props) {
  2. super(props);
  3. // 不要在这里调用 this.setState()
  4. this.state = { counter: 0 };
  5. this.handleClick = this.handleClick.bind(this);
  6. }

componentDidMount()

DOM 加载好后会被调用。通常放下面的操作:

  • 依赖于 DOM 节点的初始化操作。
  • 接口调用。

componentDidUpdate(prevProps, prevState, snapshot)

组件更新后会被调用。通常放下面的操作:

  • 监控 props 或 state 的变化。
  • 对 DOM 进行额外的操作。
    1. componentDidUpdate(prevProps) {
    2. // 典型用法(不要忘记比较 props):
    3. if (this.props.userID !== prevProps.userID) {
    4. this.fetchData(this.props.userID);
    5. }
    6. }
    注意: componentDidUpdate() 中直接调用 setState() 必须被包裹在一个条件语句里,否则会导致死循环。

componentWillUnmount()

componentWillUnmount() 会在组件卸载及销毁之前直接调用。在此方法中执行必要的清理操作,例如,清除 timer,取消网络请求或清除在 componentDidMount() 中创建的订阅等。

函数组件

Hook API

useState

存数据。

  1. import React, { useState } from 'react';
  2. function Example() {
  3. // 声明一个叫 "count" 的 state 变量
  4. const [count, setCount] = useState(0);
  5. return (
  6. <div>
  7. <p>You clicked {count} times</p>
  8. <button onClick={() => setCount(count + 1)}>
  9. Click me
  10. </button>
  11. </div>
  12. );
  13. }

useContext

  1. const ctx = React.createContext()
  2. const { Provider, Consumer } = ctx
  3. <Provider value={{name: 'joel'}}>
  4. <Child />
  5. </Provider>
  6. function Child() {
  7. const nameCtx = useContext(ctx)
  8. return <div>{nameCtx.name}</div>
  9. }

useEffect

执行副作用。

  1. useEffect(() => {
  2. fetchInitData()
  3. window.addEventListener('mouseup', handleStopResizing);
  4. return () => {
  5. window.removeEventListener('mouseup', handleStopResizing);
  6. };
  7. }, [])
  8. useEffect(() => {
  9. fetchDetail(id)
  10. }, [id])

useLayoutEffect

在所有的 DOM 变更之后同步调用 effect。可以使用它来读取 DOM 布局并同步触发重渲染。

useCallback

给子组件传函数类型的属性,要用 useCallback。

  1. const handleClick = useCallback(() => {}, [...])
  2. return (
  3. <Sub onClick={handleClick} />
  4. )

组件间通信

父子

父给子传参

props。

子通知父

调用 props 的函数。

父主动让子做些事

方案1: 主动调用。用 refs。

方案2: 子监控父组件的属性。

兄弟

共用数据的改变

状态提升。

祖先和子

祖先给子传参

方案1: 一层层往下传。

方案2: Context API
类组件:

  1. const ctx = React.createContext()
  2. const { Provider, Consumer } = ctx
  3. <Provider value={{name: 'joel'}}>
  4. <Consumer>
  5. {ctx => ( // Provider 上 value 属性的值
  6. <Component
  7. {...props}
  8. name={ctx.name}
  9. />
  10. )}
  11. </Consumer>
  12. </Provider>

函数组件

  1. const ctx = React.createContext()
  2. const { Provider, Consumer } = ctx
  3. <Provider value={{name: 'joel'}}>
  4. <Child />
  5. </Provider>
  6. function Child() {
  7. const nameCtx = useContext(ctx)
  8. return <div>{nameCtx.name}</div>
  9. }


祖先让子做些事

方案1: 主动调用。用 forwoardRef

方案2: 子监控祖先组件的属性。

组件复用

单一职责

UI 和 逻辑的分离。对应的是展示组件 和 容器组件。

多组件共用数据的分离。将数据和对数据操作的放在一起。

高阶组件(HOC)

  1. // 定义
  2. function withName (Component) {
  3. return (props) => <Component {...props} name="Joel"/>
  4. }
  5. // 使用。用装饰器看起来更舒服。
  6. const WithNameComp = withName(Comp)

Render Props

  1. // 定义
  2. function Provider (props) {
  3. return <div>{props.children({name: 'Joel', ...props})}</div>
  4. }
  5. // 使用
  6. <Provider>
  7. {(props) => {
  8. return <Comp name={props.name}/>
  9. }}
  10. </Provider>

Use Hooks

  1. // 定义
  2. import { useState, useEffect } from 'react';
  3. function useFriendStatus(friendID) {
  4. const [isOnline, setIsOnline] = useState(null);
  5. useEffect(() => {
  6. function handleStatusChange(status) {
  7. setIsOnline(status.isOnline);
  8. }
  9. ChatAPI.subscribeToFriendStatus(friendID, handleStatusChange);
  10. return () => {
  11. ChatAPI.unsubscribeFromFriendStatus(friendID, handleStatusChange);
  12. };
  13. });
  14. return isOnline;
  15. }
  16. // 使用
  17. function FriendListItem(props) {
  18. const isOnline = useFriendStatus(props.friend.id)
  19. }

文档