react 笔记 (一)

react的基本使用

安装命令:npx install react react-dom

react 包是核心,提供创建元素,组件等功能

react-dom 包提供DOM相关功能等

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- 1. 这里采用CDN 引入js文件 -->
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

    <script>
      // console.log(React); react对象
      // console.log(ReactDOM); reactDOM对象

      // 2. 创建react元素 createElement()函数
      // 参数一: 元素名称
      // 参数二: 元素属性
      // 参数三: 元素的子节点
      const title = React.createElement('h1', null, 'Hello React')

      // 3. 渲染 render() 函数
      // 参数一: 要挂载的react元素
      // 参数二: 挂载点(dom对象,要渲染到什么位置)
      ReactDOM.render(title, document.getElementById("app"));
    </script>
  </body>
</html>

jsx的基本使用

相比于使用createElement函数创建元素,jsx更直观,代码结构更清晰

// 它被称为 JSX,是一个 JavaScript 的语法扩展。
const element = <h1>Hello, world!</h1>;

在jsx中嵌入表达式

// 在下面的例子中,我们声明了一个名为 name 的变量,然后在 JSX 中使用它,并将它包裹在大括号中
const name = 'Josh Perez';
const element = <h1>Hello, {name}</h1>;

ReactDOM.render(
  element,
  document.getElementById('root')
);


// 在 JSX 语法中,你可以在大括号内放置任何有效的 JavaScript 表达式
function formatName(user) {
  return user.firstName + ' ' + user.lastName;
}

const user = {
  firstName: 'Harper',
  lastName: 'Perez'
};

const element = (
  <h1>
    Hello, {formatName(user)}!
  </h1>
);

ReactDOM.render(
  element,
  document.getElementById('root')
);

 jsx也是一个表达式

在编译之后,JSX 表达式会被转为普通 JavaScript 函数调用,并且对其取值后得到 JavaScript 对象。

也就是说,你可以在 if 语句和 for 循环的代码块中使用 JSX,将 JSX 赋值给变量,把 JSX 当作参数传入,以及从函数中返回 JSX

function getGreeting(user) {
  if (user) {
    return <h1>Hello, {formatName(user)}!</h1>;
  }
  return <h1>Hello, Stranger.</h1>;
}

jsx特定属性

// 属性值指定为字符串字面量
const element = <div tabIndex="0"></div>;

// 使用大括号,来在属性值中插入一个 JavaScript 表达式
const element = <img src={user.avatarUrl}></img>;

// 注意: 因为 JSX 语法上更接近 JavaScript 而不是 HTML,
// 所以 React DOM 使用 camelCase(小驼峰命名)来定义属性的名称,
// 而不使用 HTML 属性名称的命名约定。
// 例如,JSX 里的 class 变成了 className,而 tabindex 则变为 tabIndex。

React 组件创建的两种方式组件

1. 使用函数创建

渲染函数组件: 用函数名作为组件标签名

组件标签可以是单标签也可以是双标签

import React from 'react';
import ReactDOM from 'react-dom';

// 函数组件
// 1. 要以大写字母开头
// 2. 函数必须要有一个返回值
// function Hello() {
//   return (
//     <div>这是一个函数组件</div>
//   )
// }

// 使用箭头函数
const Hello = () => (<div>这是一个函数组件</div>)

// 3. 渲染react元素
ReactDOM.render(<Hello></Hello>, document.getElementById('root'))

2. 使用类创建组件

类组件: 使用ES6的class创建的组件

约定1: 类名称也必须以大写字母开头

约定2: 类组件应该继承React.Component父类,从而可以使用父类种提供的方法或属性

约定3: 类组件必须提供render() 方法

约定4: render() 方法必须有返回值,表达该组件的结构

import React from 'react';
import ReactDOM from 'react-dom';

// 类组件
class Hello extends React.Component {
  render() {
    return (
      <div>这是一个类组件</div>
    )
  }
}

// 3. 渲染react元素
ReactDOM.render(<Hello></Hello>, document.getElementById('root'))

3. 抽离独立js文件

  1. 创建Hello.js文件
  2. 在js文件种导入React
  3. 创建组件(函数或类)
  4. 在Hello.js种导出该组件
  5. 在index.js种导入Hello组件
  6. 渲染组件
// Hello.js
import React from 'react'

class Hello extends React.Component {
  render() {
    return <div>Hello Class Component!</div>
  }
}

export default Hello
// index.js
import React from 'react';
import ReactDOM from 'react-dom';

import Hello from './Hello'

// 3. 渲染react元素
ReactDOM.render(<Hello></Hello>, document.getElementById('root'))

 4. 有状态组件和无状态组件

函数组件又叫无状态组件,类组件又叫做有状态组件

状态state 及数据

函数组件没有自己的状态, 只负责数据展示

类组件有自己的状态,负责更新ui,让页面‘动’起来

事件处理

import React from 'react';
import ReactDOM from 'react-dom';

// 事件绑定
// React事件绑定语法与DOM事件语法相似
// on + 事件名称 = {事件处理程序}, 比如: onClick={ () => {}}
// 注意: React事件采用驼峰命名法,比如: onMouseEnter onFocus

// 函数中事件绑定
function Hello() {
  function handleClick() {
    console.log('元素发生了点击');
  }
  return <div onClick={handleClick}>开始点击吧</div>
}

// 类中事件绑定
class Hello extends React.Component {
  handleClick() {
    console.log('元素发生了点击');
  }
  render() {
    return <div onClick={this.handleClick}>类中的事件绑定</div>
  }
}

// 3. 渲染react元素
ReactDOM.render(<Hello />, document.getElementById('root'))


事件对象

import React from 'react';
import ReactDOM from 'react-dom';

// 事件对象
// 可以通过事件处理程序的参数获取到事件对象
// React中的事件对象叫做: 合成事件(对象)
// 合成事件: 兼容所有浏览器,无需担心跨浏览器兼容性问题

function handleClick(e) {
  e.preventDefault()
  console.log('事件对象',e);
}

const title = (
  <div onClick={handleClick}>点击获取事件对象</div>
)

// 渲染react元素
ReactDOM.render(title, document.getElementById('root'))


state 和 setState

state 定义数据

setState 修改数据

语法: this.setState({})

setState() 作用: 1. 修改state 2. 更新UI

import React from 'react';
import ReactDOM from 'react-dom';

// state的基本使用
class App extends React.Component {
  // constructor() {
  //   super()

  //   // 初始化state
  //   this.state = {
  //     count: 0
  //   }
  // }

  // 简化写法
  state = {
    count: 0
  }

  changeCount = () => {
    // setState修改数据
    this.setState(
     { count: this.state.count + 1}
    )
    // 错误的修改数据
    // this.state.count += 1
  }
  render() {
    return (
      <div>
        {/* 使用数据 this.state */}
        <h1>计数器:{this.state.count}</h1>
        <button onClick={this.changeCount}>+1</button>
      </div>
    )
  }
}

// 渲染react元素
ReactDOM.render(<App />, document.getElementById('root'))


 setState

setState() 是异步更新数据的

使用该语法时,后面的setState()不要依赖前面的setState()

多次调用setState(),只会触发一次重新渲染

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  state = {
    count: 1
  }
  changeCount = () => {
    this.setState({
      count: this.state.count + 1
    })
    this.setState({
      count: this.state.count + 1
    })
    console.log(this.state.count); // 1
  }
  render() {
    return (
      <div>
        {this.state.count}
        <button onClick={this.changeCount}>+1</button>
      </div>
    )
  }
}


ReactDOM.render(<App />, document.getElementById('root'))


setState的第一个参数,和第二个参数

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  state = {
    count: 1
  }
  changeCount = () => {
    // 写成回调函数形式(推荐写法)
    // state: 表示最新的state
    // props: 表示最新的props
    this.setState((state, props) => {
      return {
        count: state.count + 1
      }
    })
    this.setState(state => ({ count: state.count + 1 }))

    // setState的第二个参数 (传入一个回调函数)
    // 在状态更新(页面完成重新渲染)后立即执行
    this.setState(state => ({ count: state.count + 1 }),
      () => {
        console.log('在状态更新(页面完成重新渲染)后立即执行');
      }
    )
  }
  render() {
    return (
      <div>
        <h1 id="title">计数器: {this.state.count}</h1>
        <button onClick={this.changeCount}>+1</button>
      </div>
    )
  }
}


ReactDOM.render(<App />, document.getElementById('root'))


事件绑定this的指向

1. 箭头函数

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  state = {
    count: 1
  }
  onIncrement() {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <span>{this.state.count}</span>
        {/* {箭头函数中的this指向外部环境,此处为: render() 方法} */}
        <button onClick={() => this.onIncrement()}>+1</button>
      </div>
    )
  }
}

// 渲染react元素
ReactDOM.render(<App />, document.getElementById('root'))


2. 使用bind方法

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  constructor() {
    super()

    this.state = {
      count: 1
    }
    // 使用bind方法
    this.onIncrement = this.onIncrement.bind(this)
  }
  onIncrement() {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <span>{this.state.count}</span>
        <button onClick={this.onIncrement}>+1</button>
      </div>
    )
  }
}

// 渲染react元素
ReactDOM.render(<App />, document.getElementById('root'))


3. 事件绑定this指向 (推荐)

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  state = {
    count: 1
  }
  // 使用箭头函数,
  onIncrement = () => {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        <span>{this.state.count}</span>
        <button onClick={this.onIncrement}>+1</button>
      </div>
    )
  }
}

// 渲染react元素
ReactDOM.render(<App />, document.getElementById('root'))


表单处理

1.受控组件

React将state与表单元素值value绑定到一起,由state的值来控制元素的值

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  state = {
    // 1. 在state中添加一个状态,作为表单元素的value值(控制表单元素值的来源)
    txt: '',
    content: '',
    city: 'bj',
    isChecked: false
  }
  onIncrement = (e) => {
    this.setState({
      count: e.target.value
    })
  }
  handleContent = e => {
    this.setState({
      content: e.target.value
    })
  }
  handleCity = e => {
    this.setState({
      city: e.target.value
    })
  }
  handleIsChecked = e => {
    this.setState({
      isChecked: e.target.checked
    })
  }
  render() {
    return (
      <div>
        {/* 2. 给表单元素绑定change事件,将表单元素的值 设置为state的值(控制表单元素值的变化) */}
        {/* 文本框 */}
        <input type="text" value={this.state.txt} onChange={this.onIncrement} />
        <br />
        {/* 富文本框 */}
        <textarea value={this.state.content} onChange={this.handleContent}></textarea>
        <br />
        {/* 下拉框 */}
        <select value={this.state.city} onChange={this.handleCity}>
          <option value="sh">上海</option>
          <option value="bj">北京</option>
          <option value="gz">广州</option>
        </select>
        <br />
        {/* 复选框 */}
        <input type="checkbox" checked={this.state.isChecked} onChange={this.handleIsChecked} />
      </div >
    )
  }
}

// 渲染react元素
ReactDOM.render(<App />, document.getElementById('root'))


2. 非受控组件

import React from 'react';
import ReactDOM from 'react-dom';

class App extends React.Component {
  constructor() {
    super()

    // 调用React.createRef()函数,创建ref
    this.txtRef = React.createRef()
  }
  handleTxt = () => {
    // 通过ref对象获取文本框的值
    console.log(this.txtRef.current.value);
  }
  render() {
    return (
      <div>
        {/* 将创建好的ref对象添加到文本框中 */}
        <input type="text" ref={this.txtRef} />
        <button onClick={this.handleTxt}>点击获取表单中的值</button>
      </div>
    )
  }
}

// 渲染react元素
ReactDOM.render(<App />, document.getElementById('root'))


组件的props

组件是封闭的,要接收外部数据,应该通过props来实现

props的作用: 接受传递给组件的数据

传递数据: 给组件标签添加属性

接受数据: 函数组件通过参数props接收数据,类组件通过this.props接收数据

1. 函数组件接收数据

import React from 'react';
import ReactDOM from 'react-dom';

// props

// 2. 通过函数参数接收参数
function App(props) {
  // props是一个对象
  return (
    <div>
      <h1>传递过来的name:{props.name}</h1>
      <h1>传递过来的age:{props.age}</h1>
    </div>
  )
}

// 1. 传递数据
ReactDOM.render(<App name="jack" age={18} />, document.getElementById('root'))


2. 类组件接收数据

import React from 'react';
import ReactDOM from 'react-dom';

// props

// 2. 通过函数参数接收参数
class App extends React.Component {
  render() {
    return (
      <div>
        {/* 通过this.props 接收数据 */}
        <h1>传递过来的name: {this.props.name}</h1>
        <h1>传递过来的age: {this.props.age}</h1>
      </div>
    )
  }
}

// 1. 传递数据
ReactDOM.render(<App name="jack" age={18} />, document.getElementById('root'))


3. 组件的props特点

  1. 可以给组件传递任意类型的数据
  2. props是只读的对象,
  3. 使用类组件时,如果写了构造函数,应该将props传递给super(),否则,无法在构造函数中获取到props
import React from 'react';
import ReactDOM from 'react-dom';

// props

// 2. 通过函数参数接收参数
class App extends React.Component {
  constructor(props) {
    // 推荐将props传递给父类构造函数
    super(props)
    console.log(props.name);
  }
  render() {
    return (
      <div>
        {/* 通过this.props 接收数据 */}
        <h1>传递过来的name: {this.props.name}</h1>
        <h1>传递过来的age: {this.props.age}</h1>
      </div>
    )
  }
}

// 1. 传递数据
ReactDOM.render(<App name="jack" age={18} />, document.getElementById('root'))


4. children属性

表示组件标签的字节点,当组件标签有子节点时,props就会有该属性

与普通的props一样,值可以是任意值(文本,React元素,组件,函数)

import React from 'react';
import ReactDOM from 'react-dom';

// children属性
class App extends React.Component {
  render() {
    return (
      <div>
        组件的子节点: {this.props.children}
      </div>
    )
  }
}

ReactDOM.render(<App>我是子节点</App>, document.getElementById('root'))


5. props校验

  1. 下载 prop-types包 npm i prop-types
  2. 导入prop-types包
  3. 使用组件名.propTypes = {} 来给组件的props添加校验规则
  4. 校验规则通过PropTypes对象来指定
import React from 'react';
import ReactDOM from 'react-dom';

// 导入包
import propTypes from 'prop-types'

// children属性
const App = props => {
  const arr = props.colors
  const lis = arr.map((item, index) => <li key={index}>{item.name}</li>)
  return <ul>{lis}</ul>
}
// 添加props校验
App.propTypes = {
  // 约定colors属性为array类型
  // 如果类型不对,则报出明确的错误,便于分析错误原因
  // 常见类型 array bool func number object string
  colors: propTypes.array,
  // 必选 isRequired
  // colors: propTypes.isRequired
  // 特定结构对象
  // colors: propTypes.shape({
  //   color: propTypes.string,
  //   fontSize: propTypes.number
  // })
}

const arr = [{ name: 'coder' }, { name: 'jeka' }]

ReactDOM.render(<App colors={arr}></App>, document.getElementById('root'))


6. props的默认值 

import React from 'react';
import ReactDOM from 'react-dom';

// props的默认值
const App = (props) => {
  console.log(props);
  return (
    <div>
      props的默认值: {props.pageSize}
    </div>
  )
}

// 设置默认值
App.defaultProps = {
  pageSize: 10
}

// 当传递了,就使用传递的值
ReactDOM.render(<App pageSize={100} />, document.getElementById('root'))


组件通讯

1. 父组件传递数据给子组件

  1. 父组件提供传递的state数据
  2. 给子组件标签添加属性,值为state中的数据
  3. 子组件中通过props接收父组件中传递的数据
import React from 'react';
import ReactDOM from 'react-dom';

// 父子通讯

// 父组件
class App extends React.Component {
  state = {
    lastName: '李银河'
  }
  render() {
    return (
      <div style={{ backgroundColor: 'pink', padding: '20px' }}>
        传递数据给子组件: <Child name={this.state.lastName} />
      </div>
    )
  }
}

// 子组件
class Child extends React.Component {
  render() {
    return (
      <h1 style={{ backgroundColor: 'red' }}>
        父组件传递过来的name: {this.props.name}
      </h1>
    )
  }
}

// 1. 传递数据
ReactDOM.render(<App name="jack" age={18} />, document.getElementById('root'))


2. 子组件传递数据给父组件

  1. 利用回调函数,父组件提供回调,
  2. 子组件调用,将要传递的数据作为回调函数的参数
import React from 'react';
import ReactDOM from 'react-dom';

// 子父通讯

// 父组件
class App extends React.Component {
  state = {
    name: ''
  }
  // 1. 父组件提供回调函数
  childMsg = (data) => {
    // 4. 父组件通过参数,拿到子组件传递过来的参数
    this.setState({
      name: data
    })
  }
  render() {
    return (
      <div style={{ backgroundColor: 'pink', padding: '20px' }}>
        {/* 2. 父组件通过props把回调函数传递给子组件 */}
        子组件: <Child getMsg={this.childMsg} />
        <div>子组件传递过来的值: {this.state.name}</div>
      </div>
    )
  }
}

// 子组件
class Child extends React.Component {
  state = {
    lastName: '李银河'
  }
  render() {
    return (
      // 3. 通过props调用父组件中的回调函数,传递数据
      <h1 style={{ backgroundColor: 'red' }}
        onClick={() => this.props.getMsg(this.state.lastName)}>
        点击给父组件传递数据: {this.state.lastName}
      </h1>
    )
  }
}

// 1. 传递数据
ReactDOM.render(<App name="jack" age={18} />, document.getElementById('root'))


3. 兄弟组件通讯

将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态

import React from 'react';
import ReactDOM from 'react-dom';

// 兄弟通讯

// 父组件
class App extends React.Component {
  // 1. 父组件提供公共管理数据
  state = {
    count: 1
  }
  // 2. 父组件提供修改公共数据的方法
  changeCount = () => {
    this.setState({
      count: this.state.count + 1
    })
  }
  render() {
    return (
      <div>
        {/* 3. 通过props传递给子组件 */}
        <Child1 count={this.state.count} />
        <Child2 changeCount={this.changeCount} />
      </div>
    )
  }
}

// 子组件
class Child1 extends React.Component {
  render() {
    return (
      // 子组件使用传递的状态
      <h1>计数器: {this.props.count}</h1>
    )
  }
}
class Child2 extends React.Component {
  render() {
    return (
      // 子组件通过调用方法来修改状态
      <button onClick={() => this.props.changeCount()}>+1</button>
    )
  }
}

// 1. 传递数据
ReactDOM.render(<App name="jack" age={18} />, document.getElementById('root'))


4. Context

Context 提供了一个无需为每层组件手动添加 props,就能在组件树间进行数据传递的方法。

import React from 'react';
import ReactDOM from 'react-dom';

// 1.创建context得到两个组件
const { Provider, Consumer } = React.createContext()

class App extends React.Component {
  render() {
    return (
      <div style={{
        backgroundColor: 'red', padding: '10px'
      }}>
        {/* 2. 通过Provider组件传递数据 */}
        <Provider value='pink'>
          <Node />
        </Provider>
      </div >
    )
  }
}

class Node extends React.Component {
  render() {
    return (
      <div style={{
        backgroundColor: 'pink', padding: '10px'
      }}>
        <SubNode />
      </div >
    )
  }
}
class SubNode extends React.Component {
  render() {
    return (
      <div style={{
        backgroundColor: 'blue', padding: '10px'
      }}>
        <Child />
      </div >
    )
  }
}
class Child extends React.Component {
  render() {
    return (
      <div style={{
        backgroundColor: 'yellow', padding: '10px'
      }}>
        {/* 3. 使用Consumer使用数据 */}
        <Consumer>
          {data => <span>传递过来的数据: {data}</span>}
        </Consumer>
      </div >
    )
  }
}


ReactDOM.render(<App />, document.getElementById('root'))


组件的生命周期

import React from 'react';
import ReactDOM from 'react-dom';

// 生命周期

// 1. 创建时(挂载时执行的函数 constructor() -> render() -> componentDidMount)
// 2. 更新时 
// (当接收新的 props 时,调用setState(), forceUpdate() 都会重新执行render() 函数)
//  render() -> componentDidUpdate()
// 3. 卸载时 componentWillUnmount()
class App extends React.Component {
  constructor() {
    super()
    // 一:创建组件时,最先执行 
    // 1. 初始化state
    this.state = {
      count: 1
    }
    // 2. 为事件处理程序绑定this
    console.warn('创建组件时,最先执行')
  }

  handleClick = () => {
    // 强制更新 forceUpdate()
    this.forceUpdate()
  }
  // 钩子函数
  shouldComponentUpdate(nextProps,nextState) {
    // 更新阶段的钩子函数,在render() 之前执行
    // 返回false,阻止组件重新渲染
    // 返回true,正常渲染
    // nextProps最新的props
    // nextState最新的state
    // 
  }
  render() {
    // 二:每次组件渲染都会触发
    // 渲染UI (不能调用setState())
    console.warn('每次组件渲染都会触发')
    return (
      <div onClick={this.handleClick}>打豆豆</div>
    )
  }
  componentDidMount() {
    // 三:组件挂载(完成DOM渲染)后
    // 1. 发送网络请求
    // 2. DOM操作
    console.warn('组件挂载(完成DOM渲染)后')
  }
  conponentDidUpdate(prevProps) {
    // 四:组件更新(完成DOM渲染)后
    // 1. 发送网络请求
    // 2 DOM操作
    console.warn('组件更新(完成DOM渲染)后')
    // 如果要使用setState() 必须放在一个if条件中
    // 比较更新前后的props是否相同,来决定是否重新渲染组件
    if (prevProps.count !== this.props.count) {
      this.setState({})
    }
  }
  componentWillUnmount() {
    // 五:组件卸载 (从页面上消失)
    // 执行清理工作(比如定时器等)
    console.warn('组件卸载');
  }
}

// 当传递了,就使用传递的值
ReactDOM.render(<App pageSize={100} />, document.getElementById('root'))


render props模式

将复用的state和操作state的方法封装到一个组件中

在使用组件时,添加一个值为函数的prop,通过函数的参数来获取(需要组件内部实现)

1. render props模式

import React from 'react';
import ReactDOM from 'react-dom';

class Mouse extends React.Component {
  state = {
    x: 0,
    y: 0
  }
  handleMouseMove = e => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }

  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove)
  }
  render() {
    // 2. 调用props中的render函数,把值当参数传递进去
    return this.props.render(this.state)
  }
}
class App extends React.Component {
  render() {
    return (
      <div>
        <h1>render props 模式</h1>
        {/* 1. 通过给组件传递一个render属性,值为一个函数 */}
        <Mouse render={(mouse) => <h1>鼠标的位置:{mouse.x}, {mouse.y}</h1>} />
      </div>
    )
  }
}


ReactDOM.render(<App />, document.getElementById('root'))


2. children 替换 render (推荐)

import React from 'react';
import ReactDOM from 'react-dom';

class Mouse extends React.Component {
  state = {
    x: 0,
    y: 0
  }
  handleMouseMove = e => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }

  componentDidMount() {
    window.addEventListener('mousemove', this.handleMouseMove)
  }
  render() {
    // mouse => <h1>鼠标的位置:{mouse.x}, {mouse.y}</h1>
    console.log(this.props.children);
    // 调用传递进来的函数
    return this.props.children(this.state)
  }
}
class App extends React.Component {
  render() {
    return (
      <div>
        <h1>render props 模式</h1>
        <Mouse>
          {/* 把当前函数当作子节点传递进去 */}
          {mouse => <h1>鼠标的位置:{mouse.x}, {mouse.y}</h1>}
        </Mouse>
      </div>
    )
  }
}


ReactDOM.render(<App />, document.getElementById('root'))


高阶组件

高阶组件是一个函数, 接收要包装的组件,返回增强后的组件

高阶组件内部创建一个类组件,在这个类组件中提供复用的状态逻辑代码,通过prop将复用的状态传递给被包装组件

  1. 创建一个函数,名称约定以with开头
  2. 指定函数参数,参数应该以大写字母开头(作为要渲染的组件)
  3. 在函数内部创建一个类组件,提供复用的状态逻辑代码,并返回
  4. 在该组件中,渲染参数组件,同时将状态通过prop传递给参数组件
import React from 'react';
import ReactDOM from 'react-dom';

// 创建高阶组件
function withMouse(WrappedComponent) {
  // 该组件提供复用的状态逻辑
  return class Mouse extends React.Component {
    state = {
      x: 0,
      y: 0
    }
    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }

    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    render() {
      // 把state和props传给子props
      return <WrappedComponent {...this.state} {...this.props} />
    }
  }
}

// 鼠标位置组件
const Position = (props) => {
  return (
    <div>鼠标的位置:(x:{props.x},y:{props.y})</div>
  )
}

// 获取增强后的组件
const MounsePosition = withMouse(Position)

class App extends React.Component {
  render() {
    return (
      <div>
        <h1>高阶组件</h1>
        {/* 渲染增强后的组件 */}
        <MounsePosition />
      </div>
    )
  }
}


ReactDOM.render(<App />, document.getElementById('root'))


设置displayName

使用高阶组件存在的问题: 得到的两个组件名称相同

原因:默认情况下,React使用组件名称作为displayName

解决方法: 为高阶组件设置displayName

作用: 用于设置调式工具(React Developer Tools信息)

// 创建高阶组件
function withMouse(WrappedComponent) {
  // 该组件提供复用的状态逻辑
  class Mouse extends React.Component {
    state = {
      x: 0,
      y: 0
    }
    handleMouseMove = e => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }

    componentDidMount() {
      window.addEventListener('mousemove', this.handleMouseMove)
    }
    render() {
      // 把state传给props
      return <WrappedComponent {...this.state} />
    }
  }
  // 设置displayName
  Mouse.displayName = `WithMouse${getDisplayName(WrappedComponent)}`

  return Mouse
}

function getDisplayName(WrappedComponent) {
  return WrappedComponent.displayName || WrappedComponent.name || 'Component'
}

纯组件

纯组件和组件的区别:

  1. 自动实现了shouldComponentUpdate钩子,分别对比前后两次props和state的值(浅层对比),来决定是否重新渲染组件
import React from 'react';
import ReactDOM from 'react-dom';

// 通过继承 React.PureComponent 来创建一个纯组件
class App extends React.PureComponent {
  render() {
    return (
      <div>
        纯组件
      </div>
    )
  }
}


ReactDOM.render(<App />, document.getElementById('root'))


react 路由(6.x)

1. 路由的基本使用

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

// 下载路由
// npm i react-router-dom
// 导入路由
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'

// 登录页面
function Login() {
  return (
    <div>登录页面</div>
  )
}
// home页面
function Home() {
  return (
    <div>Home页面</div>
  )
}

function App() {
  return (
    // 配置所有路由
    <Routes>
      {/* 指定路由出口 */}
      <Route path="/login" element={<Login></Login>}></Route>
      <Route path="/" element={<Home></Home>}></Route>
    </Routes>
  )
}

ReactDOM.render(
  // 使用BrowserRouter(Router)包裹整个应用
  <Router>
    <App />
  </Router>
  ,
  document.getElementById('root')
);

2. 路由的嵌套

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

import { BrowserRouter as Router, Route, Routes, Outlet } from 'react-router-dom'

// home页面
function Home() {
  return (
    <div>Home页面:
      {/* 使用Outlet组件来指定子路由渲染的位置 */}
      <h1>显示子路由: <Outlet /> </h1>
    </div>
  )
}

// child页面
function Child() {
  return (
    <div>home页面的子页面</div>
  )
}

// child1页面
function Child1() {
  return (
    <div>home页面的子二页面</div>
  )
}

function App() {
  return (
    <Routes>
      {/* 嵌套路由 */}
      <Route path="/" element={<Home></Home>}>
        {/* index表示默认展示 只有一个index */}
        <Route index element={<Child />}></Route>
        <Route path='/child' element={<Child1 />}></Route>
      </Route>
    </Routes>
  )
}

ReactDOM.render(
  <Router>
    <App />
  </Router>
  ,
  document.getElementById('root')
);

3. 路由跳转

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

import { BrowserRouter as Router, Route, Routes, Outlet,
 useNavigate, Link } from 'react-router-dom'

// login页面
function Login() {
  // 1. 使用useNavigate()函数来跳转路由页面
  const navigate = useNavigate()
  const loginClick = () => {
    navigate('/')
  }
  return (
    <div>
      <button onClick={loginClick}>点击登录</button>
      {/* 2. 使用link标签跳转路由,to属性指定跳转路径 */}
      <Link to="/">使用link标签跳转路由</Link>
    </div>
  )
}

// home页面
function Home() {
  return (
    <div>Home页面:
      <h1>显示子路由: <Outlet /> </h1>
    </div>
  )
}


function App() {
  return (
    <Routes>
      <Route path="/" element={<Home></Home>}></Route>
      {/* 路由跳转 */}
      <Route path="/login" element={<Login />}></Route>
    </Routes>
  )
}

ReactDOM.render(
  <Router>
    <App />
  </Router>
  ,
  document.getElementById('root')
);

4. 参数的传递

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';

import { BrowserRouter as Router, Route, Routes, Outlet, useNavigate, Link, 
useParams, useSearchParams } from 'react-router-dom'

// login页面
function Login() {
  const navigate = useNavigate()
  const loginClick = () => {
    navigate('/home')
  }
  // 3. 使用useSearchParams()函数来获取请求参数
  // setSearchParams() 修改数据函数
  const [searchParams, setSearchParams] = useSearchParams()
  console.log(searchParams.get('list')); // 返回值
  console.log(searchParams.getAll('list')); // 返回一个数组
  return (
    <div>
      <button onClick={loginClick}>点击登录</button>
      {/* 在后面跟上想要传递的参数 */}
      <Link to="/home/456">使用link标签跳转路由</Link>
    </div>
  )
}

// home页面
function Home() {
  const navigate = useNavigate()
  const loginClick = () => {
    navigate('/login?list=456789&id=我的天')
  }
  // 2. 使用useParams()函数来获取动态参数
  const params = useParams()
  console.log(params.id);
  return (
    <div>Home页面:
      <button onClick={loginClick}>退出</button>
    </div>
  )
}


function App() {
  return (
    <Routes>
      {/* 使用:名字 来指定动态路由参数 */}
      <Route path="/home/:id" element={<Home></Home>}></Route>
      {/* 路由跳转 */}
      <Route path="/login" element={<Login />}></Route>
    </Routes>
  )
}

ReactDOM.render(
  <Router>
    <App />
  </Router>
  ,
  document.getElementById('root')
);

5. 使用单独文件来管理路由

// 路由懒加载
import { lazy } from 'react'
const Home = lazy(() => import('./view/Home'))
const Login = lazy(() => import('./view/Login'))
const Bar = lazy(() => import('./view/Bar'))
export const router = [
  {
    path: '/',
    element: <Home></Home>
  },
  {
    path: '/login',
    element: <Login></Login>,
    children: [
      {
        path: '/bar',
        element: <Bar />
      }
    ]
  }
]
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import { router } from './router'
import { useRoutes } from 'react-router-dom'


function App() {
  // 使用useRoutes()函数
  return useRoutes(router)
}

ReactDOM.render(
  <App />
  ,
  document.getElementById('root')
);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

派大星965

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值