受控组件、非受控组件以及高阶组件的使用

115 阅读5分钟

1. 受控组件、非受控组件的使用

1.1 自己提交form的表单

import React, { PureComponent } from "react"

export class App extends PureComponent {
  constructor() {
    super()

    this.state = {
      username: "kobe"
    }
  }
  inputChange(event) {
    console.log("inputChange: ", event.target.value)
    this.setState({ username: event.target.value }, console.log(this.state.username))
  }

  handleSubmitClick(event) {
    // 1. 阻止默认的行为
    event.preventDefault()

    // 2. 获取到所有的表单数据, 对数据进行组织
    console.log("获取所有的输入内容")

    // 3. 以网络请求的方式, 将数据传递给服务器(ajax/fetch/axios)
    console.log(this.state.username)
  }
  handleChange(event) {
    this.setState({ username: event.target.value })
  }
  render() {
    const { username } = this.state
    return (
      <div>
        // 不推荐直接在 form 表单上绑定 action,最好的方式是内部使用 button,然后在 form 上监听事件
        <form onSubmit={e => this.handleSubmitClick(e)}>
          {/* 1. 用户名和密码 */}
          <label htmlFor="username">
            用户: <input id="username" type="text" value={username} name="username" onChange={e => this.handleChange(e)} />
          </label>

          <button type="submit">注册</button>
        </form>
      </div>
    )
  }
}

export default App

1.2 多个表单使用同一个函数

import React, { PureComponent } from "react"

export class App extends PureComponent {
  constructor() {
    super()

    this.state = {
      username: "kobe",
      password: 123456
    }
  }

  handleSubmitClick(event) {
    // 1. 阻止默认的行为
    event.preventDefault()

    // 2. 获取到所有的表单数据, 对数据进行组织
    console.log("获取所有的输入内容")
    console.log(this.state.username, this.state.password)

    // 3. 以网络请求的方式, 将数据传递给服务器(ajax/fetch/axios)
  }
  handleInputChange(event) {
    const keyword = event.target.name

    this.setState({
      [keyword]: event.target.value
    })
  }
  render() {
    const { username, password } = this.state
    return (
      <div>
        <form onSubmit={e => this.handleSubmitClick(e)}>
          {/* 1. 用户名和密码 */}
          <label htmlFor="username">
            用户:
            <input id="username" type="text" value={username} name="username" onChange={e => this.handleInputChange(e)} />
          </label>
          <label htmlFor="password">
            密码:
            <input id="password" type="password" value={password} name="password" onChange={e => this.handleInputChange(e)} />
          </label>
          <button type="submit">注册</button>
        </form>
      </div>
    )
  }
}

export default App

1.3 CheckBox 的单选和多选

import React, { PureComponent } from "react"

export class App extends PureComponent {
  constructor() {
    super()

    this.state = {
      username: "kobe",
      password: 123456,
      isAgree: false,
      hobbies: [
        { value: "sing", text: "唱", isChecked: false },
        { value: "dance", text: "跳", isChecked: false },
        { value: "rap", text: "rap", isChecked: false }
      ]
    }
  }
  inputChange(event) {
    console.log("inputChange: ", event.target.value)
    this.setState({ username: event.target.value }, console.log(this.state.username))
  }

  handleSubmitClick(event) {
    // 1. 阻止默认的行为
    event.preventDefault()

    // 2. 获取到所有的表单数据, 对数据进行组织
    console.log("获取所有的输入内容")
    console.log(this.state.username, this.state.password)
    console.log(this.state.hobbies.filter(hobby => hobby.isChecked).map(hobby => hobby.value))

    // 3. 以网络请求的方式, 将数据传递给服务器(ajax/fetch/axios)
  }
  handleInputChange(event) {
    const target = event.target
    const value = target.type === "checkbox" ? target.checked : target.value
    const name = target.name
    target.type === "checkbox"
      ? this.setState({ isAgree: value })
      : this.setState({
          [name]: value
        })
  }
  handlehobbiesChange(e, index) {
    const hobbies = [...this.state.hobbies]
    hobbies[index].isChecked = e.target.checked
    this.setState({ hobbies })
  }
  render() {
    const { username, password, isAgree, hobbies } = this.state
    return (
      <div>
        <form onSubmit={e => this.handleSubmitClick(e)}>
          {/* 1. 用户名和密码 */}
          <div>
            <label htmlFor="username">
              用户:
              <input id="username" type="text" value={username} name="username" onChange={e => this.handleInputChange(e)} />
            </label>
            <label htmlFor="password">
              密码:
              <input id="password" type="password" value={password} name="password" onChange={e => this.handleInputChange(e)} />
            </label>
          </div>

          {/* 2. checkbox */}
          <div>
            <label htmlFor="agree">
              <input type="checkbox" id="agree" checked={isAgree} onChange={e => this.handleInputChange(e)} />
              同意协议
            </label>
          </div>

          {/* 3. checkbox 多选 */}
          <div>
            {hobbies.map((hobby, index) => (
              <label htmlFor={hobby.value} key={hobby.value}>
                <input id={hobby.value} type="checkbox" checked={hobby.isChecked} onChange={e => this.handlehobbiesChange(e, index)} />
                {hobby.text}
              </label>
            ))}
          </div>
          <div>
            <button type="submit">注册</button>
          </div>
        </form>
      </div>
    )
  }
}

export default App

1.4 select 的单选和多选

import React, { PureComponent } from "react"

export class App extends PureComponent {
  constructor() {
    super()

    this.state = {
      fruit: []
    }
  }

  handleSubmitClick(event) {
    // 1. 阻止默认的行为
    event.preventDefault()

    // 2. 获取到所有的表单数据, 对数据进行组织
    console.log("获取所有的输入内容")
    console.log(this.state.fruit)

    // 3. 以网络请求的方式, 将数据传递给服务器(ajax/fetch/axios)
  }
  handleFruitChange(event) {
    const options = Array.from(event.target.selectedOptions)
    const vlaues = options.map(fruit => fruit.value)
    this.setState({
      fruit: vlaues
    })

    // 额外补充: Array.from(可迭代对象)
    // Array.from(arguments)
    const vlaues2 = Array.from(event.target.selectedOptions, fruit => fruit.value)
    console.log(vlaues2)
  }
  render() {
    const { fruit } = this.state
    return (
          <div>
            <select value={fruit} multiple onChange={e => this.handleFruitChange(e)}>
              <option value="apple">苹果</option>
              <option value="orange">橘子</option>
              <option value="banana">香蕉</option>
            </select>
          </div>
          <div>
            <button type="submit">注册</button>
          </div>
        </form>
      </div>
    )
  }
}

export default App

1.5 非受控组件

import React, { createRef, PureComponent } from "react"

export class App extends PureComponent {
  constructor() {
    super()

    this.introRef = createRef()
  }

  render() {
    const { intro } = this.state
    return (
          <div>
            <input type="text" defaultValue={intro} ref={this.introRef} />
          </div>
    )
  }
}

export default App

2. 高阶组件

2.1 高阶组件的定义和作用

import React, { PureComponent } from "react"

// 定义一个高阶组件
function hoc(Cpn) {
  // 1. 定义类组件
  class NewCpn extends PureComponent {
    render() {
      return <Cpn />
    }
  }
  return NewCpn

  // 定义函数组件
  // function NewCpn2 () {

  // }
  // return NewCpn2
}

class HelloWorld extends PureComponent {
  render() {
    return <h1>Hello World</h1>
  }
}

const HelloWorldHOC = hoc(HelloWorld)

export class App extends PureComponent {
  render() {
    return (
      <div>
        <HelloWorldHOC />
      </div>
    )
  }
}

export default App

2.2 高阶组件应用-props增强

// enhanced_props.js

import { PureComponent } from "react"

function enhancedUserInfo(OriginComponent) {
  class NewComponent extends PureComponent {
    constructor() {
      super()

      this.state = {
        userInfo: {
          name: "kobe",
          level: 99
        }
      }
    }
    render() {
      const { userInfo } = this.state
      return <OriginComponent {...this.props} {...userInfo} />
    }
  }
  return NewComponent
}

export default enhancedUserInfo

// About.jsx
import React, { PureComponent } from "react"
import enhancedUserInfo from "../hoc/enhanced_props"

export class About extends PureComponent {
  render() {
    const { name, level } = this.props
    return (
      <div>
        About: {name}-{level}
      </div>
    )
  }
}

export default enhancedUserInfo(About)

import React, { PureComponent } from "react"
import enhancedUserInfo from "./hoc/enhanced_props"
import About from "./pages/About"

const Home = enhancedUserInfo(function (props) {
  return (
    <h1>
      Home: {props.name}-{props.level}-{props.banners}
    </h1>
  )
})

const Profile = enhancedUserInfo(function (props) {
  return (
    <h1>
      Profile: {props.name}-{props.level}
    </h1>
  )
})

const HelloFriend = enhancedUserInfo(function (props) {
  return (
    <h1>
      HelloFriend: {props.name}-{props.level}
    </h1>
  )
})

export class App extends PureComponent {
  render() {
    return (
      <div>
        <Home banners={["swipe1", "swipe2"]} />
        <Profile />
        <HelloFriend />
        <About />
      </div>
    )
  }
}

export default App

2.3 context 共享

// Product
import React, { PureComponent } from "react"
import ThemeContext from "../context/theme-context"
import WithTheme from "../hoc/with_theme"

// export class Product extends PureComponent {
//   render() {
//     return (
//       <div>
//         <ThemeContext.Consumer>
//           {value => {
//             return (
//               <h2>
//                 Product:{value.color}-{value.size}
//               </h2>
//             )
//           }}
//         </ThemeContext.Consumer>
//       </div>
//     )
//   }
// }

export class Product extends PureComponent {
  render() {
    const { color, size } = this.props
    return (
      <div>
        Product:{color}-{size}
      </div>
    )
  }
}

export default WithTheme(Product)

import React, { PureComponent } from "react"
import ThemeContext from "./context/theme-context"
import Product from "./pages/Product"

export class App extends PureComponent {
  render() {
    return (
      <div>
        <ThemeContext.Provider value={{ color: "red", size: 30 }}>
          <Product />
        </ThemeContext.Provider>
      </div>
    )
  }
}

export default App

2.4 登录鉴权

// login_auth
function LoginAuth(OriginComponent) {
  return props => {
    const token = window.localStorage.getItem("token")
    return token ? <OriginComponent {...props} /> : <h2>请先登录, 再跳转到其他页面</h2>
  }
}

export default LoginAuth

// Cart.jsx
import React, { PureComponent } from "react"
import LoginAuth from "../hoc/login_auth"

export class Cart extends PureComponent {
  render() {
    return <h2>Cart</h2>
  }
}

export default LoginAuth(Cart)

import React, { PureComponent } from "react"
import Cart from "./pages/Cart"

// App.jsx
export class App extends PureComponent {
  constructor() {
    super()

    this.state = {
      isLogin: false
    }
  }
  loginClick() {
    window.localStorage.setItem("token", "lwz")

    // this.setState({
    //   isLogin: true
    // })

    // 不推荐使用
    // this.forceUpdate()
  }
  render() {
    const { isLogin } = this.state
    return (
      <div>
        <button onClick={e => this.loginClick(e)}>登录</button>
        <Cart />
      </div>
    )
  }
}

export default App

2.5 生命周期

// log_render_time.js
import { PureComponent } from "react"

function logRenderTime(OriginComponent) {
  // return class NewComponent extends PureComponent {
  // 匿名组件时可以省略名称
  return class extends PureComponent {
    UNSAFE_componentWillMount() {
      this.beginTime = new Date().getTime()
    }

    componentDidMount() {
      this.endTime = new Date().getTime()
      const interval = this.endTime - this.beginTime
      console.log(`当前页面${OriginComponent.name}花费了${interval}ms来渲染!`)
    }

    render() {
      return <OriginComponent {...this.props} />
    }
  }
}

export default logRenderTime

// Detail
import React, { PureComponent } from "react"
import logRenderTime from "../hoc/log_render_time"

export class Detail extends PureComponent {
  render() {
    return (
      <div>
        <h2>Detail Page</h2>
        <ul>
          <li>页面列表1</li>
          <li>页面列表2</li>
          <li>页面列表3</li>
          <li>页面列表4</li>
          <li>页面列表5</li>
          <li>页面列表6</li>
          <li>页面列表7</li>
          <li>页面列表8</li>
          <li>页面列表9</li>
          <li>页面列表10</li>
        </ul>
      </div>
    )
  }
}

export default logRenderTime(Detail)

// App.jsx
import React, { PureComponent } from "react"
import Detail from "./pages/Detail"

export class App extends PureComponent {
  // UNSAFE_componentWillMount() {
  //   this.beginTime = new Date().getTime()
  // }

  // componentDidMount() {
  //   this.endTime = new Date().getTime()
  //   const interval = this.endTime - this.beginTime
  //   console.log(`当前页面花费了${interval}ms来渲染`)
  // }

  render() {
    return (
      <div>
        <Detail />
      </div>
    )
  }
}

export default App

3. React 的 Protals

// index.html
<style>
  #modal {
    position: fixed;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
  }
</style>
<body>
  <div id="root"></div>
  <div id="lwz"></div>
  <div id="modal"></div>
</body>

// App.jsx
import React, { PureComponent } from "react"
import { createPortal } from "react-dom"
import Modal from "./Modal"

export class App extends PureComponent {
  render() {
    return (
      <div>
        <h1>Hello World</h1>
        {createPortal(<h2>Hello React</h2>, document.querySelector("#lwz"))}

        {/* 2. Modal 组件 */}
        <Modal>
          <h2>我是标题</h2>
          <p>我是内容</p>
        </Modal>
      </div>
    )
  }
}

export default App

// Modal.jsx
import { PureComponent } from "react"
import { createPortal } from "react-dom"

export class Modal extends PureComponent {
  render() {
    return createPortal(this.props.children, document.querySelector("#modal"))
  }
}

export default Modal

4. React 的 fragment

import React, { Fragment, PureComponent } from "react"

export class App extends PureComponent {
  render() {
    return (
      // 可以简写称 <></>
      // 当需要绑定 key 值的时候不能简写
      <Fragment>
        <h2>Hello World</h2>
        <h2>Hello React</h2>
      </Fragment>
    )
  }
}

export default App

5. StrictModel

5.1 作用

  • StrictMode 是一个用来突出显示应用程序中潜在问题的工具:
    • 与 Fragment 一样,StrictMode 不会渲染任何可见的 UI;
    • 它为其后代元素触发额外的检查和警告;
    • 严格模式检查仅在开发模式下运行;它们不会影响生产构建;

5.2 检查内容

  1. 识别不安全的生命周期:
  2. 使用过时的ref API
  3. 检查意外的副作用
    • 这个组件的constructor会被调用两次;
    • 这是严格模式下故意进行的操作,让你来查看在这里写的一些逻辑代码被调用多次时,是否会产生一些副作用;
    • 在生产环境中,是不会被调用两次的;
  4. 使用废弃的findDOMNode方法
    • 在之前的React API中,可以通过findDOMNode来获取DOM,不过已经不推荐使用了,可以自行学习演练一下
  5. 检测过时的context API
    • 早期的Context是通过static属性声明Context对象属性,通过getChildContext返回Context对象等方式来使用Context的;
    • 目前这种方式已经不推荐使用,大家可以自行学习了解一下它的用法;