React 使用 - 图1

官网:

一、JSX 使用

JSX 是 JavaScript 的语法扩展,描述 UI 呈现出它应有交互的本质形式。可拆分为多行,内容包裹在括号中。

  • JSX 表示对象:Babel 会把 JSX 转译成React.createElement(component, props, ...children)函数调用,创建对象被称为 “React 元素”。
  • JSX 是一个表达式:if 语句、for 循环不是表达式不能在 JSX 中直接使用。可使用 conditional (三元运算) 表达式来替代 ```javascript import React from ‘react’

class JSXBaseDemo extends React.Component { constructor(props) { super(props) this.state = { name: ‘咕噜噜’, flag: true, imgUrl: ‘https://cdn.nlark.com/yuque/0/2019/jpeg/anonymous/1574041777259-ab793e4f-3fe2-413c-bbc8-81db193932da.jpeg?x-oss-process=image%2Fresize%2Cm_fill%2Cw_320%2Ch_320%2Fformat%2Cpng‘ } }

render() { // // 变量 // const ele =

{this.state.name}

// // // 表达式 // const exp =

{this.state.flag ? ‘yes’ : ‘no’}

// const imgEle = ( //
//

我的头像

// React 使用 - 图2 // //
// )

  1. // // class
  2. // const classEle = <p className="title">title</p>
  3. //
  4. // // style
  5. // const styleData = { fontSize: '30px', color: 'blue' }
  6. // const styleEle = <p style={styleData}>style</p>
  7. // // 内联写法
  8. // const styleEle = <p style={{ fontSize: '30px', color: 'blue' }}>style</p>
  9. return <div>hello world</div>

} }

export default JSXBaseDemo

  1. <a name="uz69B"></a>
  2. ### 1、变量、表达式
  3. ```javascript
  4. render() {
  5. // 变量
  6. const ele = <p>{this.state.name}</p>
  7. // 表达式
  8. const exp = <p>{this.state.flag ? 'yes' : 'no'}</p>
  9. const imgEle = (
  10. <div>
  11. <p>我的头像</p>
  12. <img src={this.state.imgUrl} />
  13. </div>
  14. )
  15. }

2、class、style

  1. render() {
  2. // class
  3. const classEle = <p className="title">title</p>
  4. // style
  5. const styleData = { fontSize: '30px', color: 'blue' }
  6. const styleEle = <p style={styleData}>style</p>
  7. // 内联写法
  8. const styleEle = <p style={{ fontSize: '30px', color: 'blue' }}>style</p>
  9. }

3、子元素和组件

  1. render() {
  2. const imgEle = (
  3. <div>
  4. <p>我的头像</p>
  5. <img src={this.state.imgUrl} />
  6. <MyComponent />
  7. </div>
  8. )
  9. }

二、组件、props、state

1、基本概念

组件 Vue React
定义 可复用的 Vue 实例 Props 作为参数返回 React 元素的 JavaScript 函数
命名 推荐使用 kebab-case 必须以大写字母开头
VirtualDOM 挂载 实例 render 函数创建虚拟节点 VNode,如render: h => h(App)
,el 选项或 vm.$mount() 再将其挂载在 DOM 节点
函数 render 方法返回 React 元素,再由 ReactDOM 的 render 方法将其挂载到 DOM 节点

2、组件分类

2.1 class 组件

  • 定义:作为 React.Component 子类,通过 props 从父组件向子组件传递数据
  • 渲染方法:render()方法是类组件中唯一需要的方法,用于渲染DOM节点
  • 构造函数:初始化 state 或进行方法绑定,一定需要调用 super(props)
    • 通过给 this.state 赋值对象来初始化内部 state。
    • 为事件处理函数绑定实例
  • 状态维护:state 用于组件状态维护,被视为一个组件的私有属性
    • 每次定义子类的构造函数时,都需要调用 super 方法。即 super(props) 开头
    • 每次在组件中调用 setState 时,React 都会自动更新其子组件
  • 状态提升:需共享 state 向上移动到最近共同父组件中的 state 中用作“数据源”,即“状态提升”。任何可变数据应当只有一个相对应的唯一“数据源”。
    • 受控组件:由 React 控制并且所有的表单数据统一存放的组件。响应数据改变时,子组件调用 this.props.onChange() 而非 this.setState()。
    • 不可变性:不直接修改原数据/底层数据以便跟踪数据的改变,确定在 React 中何时重新渲染
  1. class Square extends React.Component {
  2. render() {
  3. return (
  4. <button className="square" onClick={() => this.props.onClick()}>
  5. {this.props.value}
  6. </button>
  7. );
  8. }
  9. }
  10. class Board extends React.Component {
  11. constructor(props) {
  12. super(props);
  13. this.state = {
  14. squares: Array(9).fill(null)
  15. };
  16. }
  17. handleClick(i) {
  18. const squares = this.state.squares.slice();
  19. squares[i] = "X";
  20. this.setState({ squares: squares });
  21. }
  22. renderSquare(i) {
  23. return (
  24. <Square
  25. value={this.state.squares[i]}
  26. onClick={() => this.handleClick(i)}
  27. />
  28. );
  29. }
  30. render() {
  31. return (
  32. <div>
  33. {this.renderSquare(0)}
  34. {this.renderSquare(1)}
  35. {this.renderSquare(2)}
  36. </div>
  37. );
  38. }
  39. }

2.2 函数组件

  • 定义:不需定义一个继承于 React.Component 的类,可定义一个接收 props 作为参数的函数,然后返回需要渲染的元素
  • 使用场景:只包含一个 render 方法,不包含 state
  1. //把两个 this.props 都替换成了 props,注意两侧括号不再有,不需要再处理 this
  2. function Square(props){
  3. render(){
  4. return (
  5. <button className="square" onClick={props.onClick}>
  6. {props.value}
  7. </button>
  8. )
  9. }
  10. }

3、Props 的只读性

组件无论是使用函数声明还是通过 class 声明,都决不能修改自身的 props所有 React 组件都必须像纯函数一样保护其 props 不被更改。但状态 state 允许组件随时间更改其输出以响应用户操作,网络响应以及其他任何情况。

4、State

State 与 props 类似,但是 state 是私有的,并且完全受控于当前组件。

  • 不要直接修改 State,应该拷贝一份再使用 setState()
  • this.props 和 this.state 可能会异步更新,不要依赖他的值来更新下一个状态。可以让 setState() 接收一个函数而不是一个对象,setTimeout 和自定义 DOM 事件中是同步更新
  • State 的更新会被合并。调用 setState() 的时候,React 会把你提供的对象合并到当前的 state
  • 自上而下数据流:任何状态始终归某个特定组件所有,且从该状态派生的任何数据或UI都只影响树中“下方”的组件
  • 如果某些数据可以由 props 或 state 推导得出,那么它就不应该存在于 state 中
  1. // 场景一:setState 接收函数
  2. // 函数中第一个参数为上一个 state,第二个为此次更新被应用时的 props
  3. this.setState((prevState, nextprops) => ({
  4. counter: prevState.counter + nextprops.increment
  5. }));
  6. // 场景二:setState 回调函数
  7. this.setState({
  8. count: this.state.count + 1
  9. }, () => {
  10. console.log('callback:', this.state.count) // 回调函数获取的是更新后的值
  11. })
  12. console.log('count:' this.state.count) // 异步的,拿不到最新值
  13. // 场景三:setTimeout 或自定义 DOM 事件中的 setState 是同步的
  14. // setTimeout
  15. setTimeout(() => {
  16. this.setState({
  17. count: this.state.count + 1
  18. })
  19. console.log('count:' this.state.count) // 可获取最新值
  20. }, 0)
  21. // 自定义 DOM 事件
  22. document.body.addEventListener('click', () => {
  23. this.setState({
  24. count: this.state.count + 1
  25. })
  26. console.log(this.state.count)
  27. })

5、生命周期

旧版生命周期
Old_Lifecycle.png
新版生命周期
New_Lifecycle.png

三、条件

1、if else

  1. render() {
  2. if (this.state.theme === 'black') {
  3. return <div>black</div>
  4. } else {
  5. return <div>white</div>
  6. }
  7. }

2、三元表达式

  1. render() {
  2. return (
  3. <div>
  4. {this.state.theme === 'black' ? <div>black</div> : <div>white</div>}
  5. </div>
  6. )
  7. }

3、逻辑运算符( &&、|| )

  1. render() {
  2. return (
  3. <div>
  4. {this.state.theme === 'black' && <div>black</div>}
  5. </div>
  6. )
  7. }

四、列表渲染

1、map

  1. render() {
  2. return (
  3. <ul>
  4. {this.state.list.map(item => <li key={item.id}>{item.title}</li>)}
  5. </ul>
  6. )
  7. }

2、key

key 属性必须保证其在同级中是唯一的,建议不要用 index 和 random

五、事件

  • 需使用小驼峰命名,如:onClick、onChange
  • 需要在构造函数中绑定 this 指向(或使用箭头函数)
  • 只能显示阻止默认行为:handleClick(e){ e.preventDefault() }
  • react 中的事件是 SyntheticBaseEvent 合成事件,所有事件都挂载在 document 上(和 DOM 事件不同) ```javascript import React from ‘react’

class JSXBaseDemo extends React.Component { constructor(props) { super(props) this.state = { name: ‘hello’ } // 修改方法的 this 指向 this.handleClick = this.handleClick.bind(this) }

handleClick(event) { this.setState({ name: ‘world’ }) console.log(event.target) // 指向当前元素,即当前元素触发 console.log(event.currentTarget) // 指向当前元素,假象,并非绑定在当前元素,react 合成事件特殊处理 console.log(event.nativeEvent) // 原生事件对象 console.log(event.nativeEvent.target) // 指向当前元素,即当前元素触发 console.log(event.nativeEvent.currentTarget) // 指向 document,即绑定在 document 上 }

// 箭头函数写法 // handleClick = () => { // this.setState({ // name: ‘world’ // }) // }

render() { return

{this.state.name}
} }

export default JSXBaseDemo

  1. <a name="GDNhw"></a>
  2. ## 六、表单
  3. React 中,表单状态通常保存在组件的 state 属性中,且只能通过 setState() 来更新
  4. <a name="6PV03"></a>
  5. ### 1、受控组件
  6. React 中 state 是数据源,被 React **控制取值**的表单输入元素叫做“受控组件”,如 input 的 value 属性由 state 控制
  7. ```javascript
  8. import React from 'react'
  9. class JSXBaseDemo extends React.Component {
  10. constructor(props) {
  11. super(props)
  12. this.state = {
  13. name: 'hello'
  14. }
  15. }
  16. handleChange = (e) => {
  17. this.setState({
  18. name: e.target.value
  19. })
  20. }
  21. render() {
  22. return (
  23. <div>
  24. <p>{this.state.name}</p>
  25. <label htmlFor="inputName">姓名:</label> {/* htmlFor 代替 for */}
  26. <input id="inputName" value={this.state.name} onChange={this.handleChange} />
  27. </div>
  28. )
  29. }
  30. }
  31. export default JSXBaseDemo

2、常用表单标签

  • input textarea select 用 value 控制值
  • checkbox radio 用 checked 控制值

    1. render() {
    2. return (
    3. <div>
    4. <input value={this.state.name} onChange={this.handleChange} />
    5. <textarea value={this.state.name} onChange={this.handleChange} />
    6. <select value={this.state.name} onChange={this.handleChange}>
    7. <option value="beijing">北京</option>
    8. <option value="guangzhou">广州</option>
    9. </select>
    10. <input type="checkbox" checked={this.state.name} onChange={this.handleChange} />
    11. </div>
    12. )
    13. }

    七、高级用法

    1、非受控组件

    在一个受控组件中,表单数据是由 React 组件来管理的。使用非受控组件时表单数据将交由 DOM 节点来处理
    使用场景:必须手动操作 DOM 元素,setState 实现不了

  • 要编写一个非受控组件,而不是为每个状态更新都编写数据处理函数,你可以 使用 ref 来从 DOM 节点中获取表单数据

  • 非受控组件将真实数据储存在 DOM 节点中,所以在使用非受控组件时,有时候反而更容易同时集成 React 和非 React 代码
  • 默认值:非受控组件中赋予组件一个初始值,但是不去控制后续的更新。 可指定一个 defaultValue 属性,而不是 value。
  • 文件输入:在 React 中,<input type="file" /> 始终是一个非受控组件,因为它的值只能由用户设置,而不能通过代码控制 ```javascript import React from ‘react’

class App extends React.Component { constructor(props) { super(props) this.state = { name: ‘123’, flag: true, } this.nameInputRef = React.createRef() // 创建 ref } render() { // input defaultValue return

{/ 使用 defaultValue 而不是 value ,使用 ref /} {/ state 并不会随着改变 /} state.name: {this.state.name}

  1. }
  2. alertName = () => {
  3. const elem = this.nameInputRef.current // 通过 ref 获取 DOM 节点
  4. alert(elem.value) // 不是 this.state.name
  5. }

}

export default App

  1. <a name="5w54b"></a>
  2. ### 2、Portals
  3. Portal 提供了一种将子节点渲染到存在于父组件以外的 DOM 节点的优秀的方案`ReactDOM.createPortal(child, container)`
  4. - 一个 portal 的典型用例是当父组件有 overflow: hidden 或 z-index 样式时,但你需要子组件能够在视觉上“跳出”其容器。例如,对话框、悬浮卡以及提示框
  5. - 当在使用 portal 时, 管理键盘焦点尤为重要
  6. - 通过 Portal 进行事件冒泡:一个从 portal 内部触发的事件会一直冒泡至包含 React 树的祖先
  7. ```javascript
  8. render() {
  9. // React 并没有创建一个新的 div。它只是把子元素渲染到 domNode 中
  10. // domNode 是一个可以在任何位置的有效 DOM 节点。
  11. return ReactDOM.createPortal(
  12. this.props.children,
  13. domNode
  14. );
  15. }

3、Context

将数据向组件树下所有组件“广播”,共享组件树全局数据以便不同层级的组件访问

  1. import React from 'react'
  2. // 创建 Context 填入默认值(任何一个 js 变量)
  3. const ThemeContext = React.createContext('light')
  4. // 底层组件 - 函数是组件
  5. function ThemeLink (props) {
  6. // const theme = this.context // 会报错。函数式组件没有实例,即没有 this
  7. // 函数式组件可以使用 Consumer
  8. return <ThemeContext.Consumer>
  9. { value => <p>link's theme is {value}</p> }
  10. </ThemeContext.Consumer>
  11. }
  12. // 底层组件 - class 组件
  13. class ThemedButton extends React.Component {
  14. // 指定 contextType 读取当前的 theme context。
  15. // static contextType = ThemeContext // 也可以用 ThemedButton.contextType = ThemeContext
  16. render() {
  17. const theme = this.context // React 会往上找到最近的 theme Provider,然后使用它的值。
  18. return <div>
  19. <p>button's theme is {theme}</p>
  20. </div>
  21. }
  22. }
  23. ThemedButton.contextType = ThemeContext // 指定 contextType 读取当前的 theme context。
  24. // 中间的组件再也不必指明往下传递 theme 了。
  25. function Toolbar(props) {
  26. return (
  27. <div>
  28. <ThemedButton />
  29. <ThemeLink />
  30. </div>
  31. )
  32. }
  33. class App extends React.Component {
  34. constructor(props) {
  35. super(props)
  36. this.state = {
  37. theme: 'light'
  38. }
  39. }
  40. render() {
  41. return <ThemeContext.Provider value={this.state.theme}>
  42. <Toolbar />
  43. <hr/>
  44. <button onClick={this.changeTheme}>change theme</button>
  45. </ThemeContext.Provider>
  46. }
  47. changeTheme = () => {
  48. this.setState({
  49. theme: this.state.theme === 'light' ? 'dark' : 'light'
  50. })
  51. }
  52. }
  53. export default App

4、异步组件(懒加载)

  • import()
  • React.lazy
  • React.Suspense ```javascript import React from ‘react’

const ContextDemo = React.lazy(() => import(‘./ContextDemo’))

class App extends React.Component { constructor(props) { super(props) } render() { return

引入一个动态组件


Loading…
}> } }

export default App

  1. <a name="W3plt"></a>
  2. ### 5、性能优化
  3. <a name="Acjn8"></a>
  4. #### 5.1 shouldComponentUpdate(简称 SCU)
  5. - SCU 默认返回 true,即 React 默认重新渲染所有子组件
  6. - 必须配合**不可变值**一起使用
  7. - 可先不用 SCU,有性能问题时再做考虑
  8. ```javascript
  9. shouldComponentUpdate(nextProps, nextState) {
  10. if (nextState.count !== this.state.count) {
  11. return true // 可以渲染
  12. }
  13. return false // 不重复渲染
  14. }

5.2 PureComponent 和 React.memo

  • PureComponents(纯组件),SCU 中实现了浅比较 ```javascript // 以下两种方式效果相同 class App extends React.PureComponent { // … }

class App extends React.Component { shouldComponentUpdate(nextProps, nextState) { // 浅比较 } // … }

  1. - memo 是函数组件中的 PureComponents
  2. ```javascript
  3. function MyComponent(props) {
  4. // 使用 props 渲染
  5. }
  6. function shouldUpdate(prevProps, nextProps) {
  7. // 返回结果为 true,则重新渲染;返回 false,则不渲染
  8. }
  9. export default React.memo(MyComponent, shouldUpdate)
  • 浅比较可在大部分情况使用

5.3 不可变值 immutable.js

  • 彻底拥抱不可变
  • 基于共享数据(不是深拷贝),速度好 ```javascript import { Map } from ‘immutable’; const map1 = Map({ a: 1, b: 2, c: 3 }) const map2 = map1.set(‘b’, 50)

// map1 和 map2 共享了没有变化的 a 和 c 节点 map1 === map2 // false map1.get(‘b’) // 2 map2.get(‘b’) // 50 map1.get(‘a’) // 1 map2.get(‘a’) // 1

  1. <a name="S5SSI"></a>
  2. ### 6、高阶组件
  3. 高阶组件(HOC)是一种复用状态逻辑的技巧,它参数为组件,返回值为新组件的函数。编写一个创建组件函数。该函数将接受一个子组件作为它的其中一个参数,该子组件将订阅数据作为 prop
  4. <a name="sX0RS"></a>
  5. #### 6.1 基本用法
  6. ```javascript
  7. // 高阶组件不是一种功能,而是一种模式
  8. const HOCFactory = (Component) => {
  9. class HOC extends React.Component {
  10. // 在此定义多个组件的公共逻辑
  11. render() {
  12. return <Component {...this.props} /> // 返回拼装的结果
  13. }
  14. }
  15. return HOC
  16. }
  17. const EnhancedComponent1 = HOCFactory(WrappedComponent1)
  18. const EnhancedComponent2 = HOCFactory(WrappedComponent2)

6.2 实际举例

通过高阶组件增加通用逻辑——获取鼠标位置

  1. // 高阶组件
  2. const withMouse = (Component) => {
  3. class withMouseComponent extends React.Component {
  4. constructor(props) {
  5. super(props)
  6. this.state = { x: 0, y: 0 }
  7. }
  8. handleMouseMove = (event) => {
  9. this.setState({
  10. x: event.clientX,
  11. y: event.clientY
  12. })
  13. }
  14. render() {
  15. return (
  16. <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
  17. // 1. 透传所有 props
  18. // 2. 增加 mouse 属性
  19. <Component {...this.props} mouse={this.state}/>
  20. </div>
  21. )
  22. }
  23. }
  24. return withMouseComponent
  25. }
  26. const App = (props) => {
  27. const a = props.a
  28. const { x, y } = props.mouse // 接收 mouse 属性
  29. return (
  30. <div style={{ height: '500px' }}>
  31. <h1>The mouse position is ({x}, {y})</h1>
  32. <p>{a}</p>
  33. </div>
  34. )
  35. }
  36. export default withMouse(App) // 返回高阶函数

7、Render Props

术语 “render prop” 是指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术;它的作用是分享一个组件封装到其他需要相同 state 组件的状态或行为,也是一种复用状态逻辑的方式

具有 render prop 的组件接受一个函数,该函数返回一个 React 元素并调用它而不是实现自己的渲染逻辑

  1. import React from 'react'
  2. class Mouse extends React.Component {
  3. constructor(props) {
  4. super(props)
  5. this.state = { x: 0, y: 0 }
  6. }
  7. handleMouseMove = (event) => {
  8. this.setState({
  9. x: event.clientX,
  10. y: event.clientY
  11. })
  12. }
  13. render() {
  14. return (
  15. <div style={{ height: '500px' }} onMouseMove={this.handleMouseMove}>
  16. {/* 将当前 state 作为 props ,传递给 render (render 是一个函数组件) */}
  17. {this.props.render(this.state)}
  18. </div>
  19. )
  20. }
  21. }
  22. const App = (props) => (
  23. <div style={{ height: '500px' }}>
  24. <Mouse render={
  25. /* render 是一个函数组件 */
  26. ({ x, y }) => <h1>The mouse position is ({x}, {y})</h1>
  27. }/>
  28. </div>
  29. )
  30. /**
  31. * 即,定义了 Mouse 组件,只有获取 x y 的能力。
  32. * 至于 Mouse 组件如何渲染,App 说了算,通过 render prop 的方式告诉 Mouse 。
  33. */
  34. export default App

8、Refs 及其转发

  • Refs 提供一种方式,允许访问 DOM 节点或在 render 方法中创建的 React 元素
  • Ref 转发是将 ref 自动地通过组件传递到其一子组件,常用于可重用的组件库

8.1 使用 Refs

  • 何时使用:管理焦点,文本选择或媒体播放;触发强制动画;集成第三方DOM
  • 创建 refs:React.createRef() 创建并通过 ref 属性附加到 React 元素

    1. class MyComponent extends React.Component {
    2. constructor(props) {
    3. super(props);
    4. this.myRef = React.createRef();
    5. }
    6. render() {
    7. return <div ref={this.myRef} />;
    8. }
    9. }
    10. //构造组件时,通常将 Refs 分配给实例属性,以便在整个组件中引用它们
  • 访问 refs:ref 被传递给 render 中的元素时,由this.myRef.current引用

    • ref 属性用于 HTML 元素时,ref 接收底层 DOM 元素为其current属性
    • ref 属性用于自定义 class 组件时,ref 接收组件挂载实例为其current 属性
    • 不能在函数组件上使用 ref 属性,因为他们没有实例
    • 可以在函数组件内部使用 ref 属性,只要它指向 DOM 元素或 class 组件
  • 回调 Refs:传递接受 React 组件实例或 HTML DOM 元素作为参数的函数。
  1. function CustomTextInput(props) {
  2. return (
  3. <div>
  4. <input ref={props.inputRef} />
  5. </div>
  6. );
  7. }
  8. class Parent extends React.Component {
  9. render() {
  10. return (
  11. <CustomTextInput
  12. inputRef={el => this.inputElement = el}
  13. />
  14. );
  15. }
  16. }
  17. //Parent 中的 this.inputElement 会被设置为与 input 元素相对应的 DOM 节点

8.2 转发 refs 到 DOM 组件

Ref 转发是可选特性,其允许某些组件接收 ref,并将其向下传递给子组件。FancyButton 使用 React.forwardRef 来获取传递给它的 ref,然后转发到它渲染的 DOM button

  • 调用 React.createRef 创建一个 React ref 并将其赋值给 ref 变量。
  • 指定 ref 为 JSX 属性,将其向下传递给 <FancyButton ref={ref}>
  • React 传递 ref 给 forwardRef 内函数 (props, ref) => …,作为其第二个参数。
  • 向下转发该 ref 参数到 <button ref={ref} />,将其指定为 JSX 属性。
  • ref 挂载完成,ref.current 将指向 <button> DOM 节点
  1. const FancyButton = React.forwardRef((props, ref) => (
  2. <button ref={ref} className="FancyButton">
  3. {props.children}
  4. </button>
  5. ));
  6. // 你可以直接获取 DOM button 的 ref:
  7. const ref = React.createRef();
  8. <FancyButton ref={ref}>Click me!</FancyButton>;

八、Redux

1、单向数据流图

redux单向数据流.png