React入门指南 基础篇(一)

468 阅读8分钟

前言

现在找工作市场上Vue和react基本上是55开,但在一线城市大部分以react技术栈开发为主,只会vue是远远不够的,这迫使我们不得不学,此篇是学习后整理出来的,算不上是文章,只能说是学习完后整理的笔记,使用通过易懂的文字加上案例代码,让小白学习起来更轻松,希望对刚学习react的小白有所帮助,ps:如有错误,请大佬评论指正

react是什么?

react是一个用于构建用户界面的 JavaScript 库,具有声明式,组件化的特点

react官网

react中文官网

react新版官网

react的基础安装

在使用react之前需要引入reactreact-dom两个插件,react用于提供常见元素,组件等功能,react-dom是用于操作dom相关的功能

CDN引入

开发环境:

<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 crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

npm下载

npm i react react-dom

元素创建&渲染

// 1.引入CDN
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="root"></div>

// 2.利用React.createElement来进行创建元素  参数一:元素名称  参数二:元素属性 参数三:元素子节点
const helloReact = React.createElement('h1', null, 'hello React')

// 3.利用ReactDOM.render来渲染元素 参数一:要渲染的元素 参数二:挂载点
ReactDOM.render(helloReact, document.getElementById('root'))

脚手架的安装与使用

react脚手架可以帮我们生成一个通用的目录结构,并且已经将我们所需要的工程环境配置好,创建命令如下

安装

  • npm init create-app my-react
  • yarn create react-app my-react
  • npx create-react-app my-app (npm 5.2+才可使用) 使用使用方法同上一样,只是导入的方式不一样
import React from 'react';
import ReactDOM from 'react-dom';
const helloReact = React.createElement('h1', null, 'hello React')
ReactDOM.render(helloReact, document.getElementById('root'))

JSX

JSX是一种JavaScript的语法扩展,可以让我们高效的开发,使用以下代码与上面创建元素的代码效果是等同的,我们将JSX代码放入react中,实际上是经过bable编译之后再放入浏览器上运行的,编译之后的代码其实和上面写的代码一样,尤其是编写树状结构的时候,可以一眼看出,使用JSX来书写更加简洁,结构清晰,更加高效!

注意点:属性名使用驼峰命名

  • class--->className
  • for--->htmlFor
  • tabindex ---> tabIndex 元素创建&渲染
const helloJSX = (<h1 className="JSX">hello JSX</h1>)
ReactDOM.render(helloJSX,document.getElementById('root'))

条件渲染

if/else

const loading = false
const loadData = () => {
  if (loading) {
    return (<div>数据加载中...</div>)
  }
  return (<div>数据加载完成...</div>)
}
ReactDOM.render(loadData(),document.getElementById('root'))

三元表达式

const loading = true
const loadData = () => {
 return loading && (<div>数据加载中...</div> )
}
ReactDOM.render(loadData(), document.getElementById('root'))

与运算符

// 与运算符
const loading = true
const loadData = () => {
 return loading && (<div>数据加载中...</div> )
}
ReactDOM.render(loadData(), document.getElementById('root'))

列表渲染

const nameList = [{ id:1, name: '小明', age:4 },{ id:2, name: '小刚', age:18 },{ id:3, name: '小红', age:6 }]
const list = (
  <ul>
    {nameList.map(item => <li key={item.id}>{ item.name}</li>)}
  </ul>
)
ReactDOM.render(list, document.getElementById('root')

组件&事件绑定

组件分为有状态组件无状态组件,存放数据的state则为状态,在react v16.8.0之前,函数组件是没有状态的,在react v16.8.0之后有了hook才有状态,一般函数组件只负责数据展示,类组件实现数据与页面动态更新。

函数组件

// 创建函数组件
function FirstComponent (){
  return (<div>函数组件</div>)
}
ReactDOM.render(<FirstComponent />, document.getElementById('root'))

类组件

// 创建类组件
class FirstComponent extends React.Component{
  render(){
    return (<div>类组件</div>)
  }
}
ReactDOM.render(<FirstComponent />, document.getElementById('root'))

组件事件绑定

class TitleClick extends React.Component{
  handerClick (e) {
  // 利用事件对象阻止跳转
    e.preventDefault()
    console.log(123);
  }
  render () {
  // 添加点击事件onClick
    return (<a href='https://www.baidu.com/' onClick={this.handerClick}>点击我</a>)
  }
}

state&props

在组件中使用state来定义组件状态,通过this.setState()来修改组件状态,state只能在组件内部使用。如果在子组件中使用负组件的数据则需要使用props接收使用,在使用props的时候需要注意:props是只读对象,无法修改,如果在类组件中手动写了constructor时,需要将props传递给super(),不然无法获取props

class Counter extends React.Component{
// 1.定义组件状态
  state = {
    num:1
  }
  render () {
    return (
      <div>
        <div>计数器:{this.state.num}</div>
        <button onClick={() => {
        // 2.修改组件状态
          this.setState({
            num:this.state.num + 1
          })
      }}>+1</button>
      </div>
    )
  }
}

组件传值

父组件--->子组件

import { Component } from "react";

export class Father extends Component {
  state = {
    fatherVlaue: '父组件数据'
  }
  render () {
    return (<div><Son {...this.state} /></div>)
  }
}

class Son extends Component {
  render () {
    return (<div>{this.props.fatherVlaue}</div>)
  }
}

子组件--->父组件

import { Component } from "react";

export class Father extends Component {
  state = {
    value: '父组件数据'
  }
  fatherHandeValue = (value) => {
    this.setState({ value })
  }
  render () {
    return (<div>{this.state.value}<Son sonHandeValue={this.fatherHandeValue} /></div>)
  }
}

class Son extends Component {
  state = {
    sonValue: '子组件数据'
  }
  sonHandeValue = () => {
    this.props.sonHandeValue(this.state.sonValue)
  }
  render () {
    return (<div><button onClick={this.sonHandeValue}>点击传值</button></div>)
  }
}


兄弟传值

import { Component } from 'react'
export class Father extends Component {
  state = {
    value: ''
  }
  fatherHandeValue = (value) => {
    this.setState({ value })
  }
  render () {
    return (<div>
      <Children1 sonHandeValue={this.fatherHandeValue} />
      <Children2 value={this.state.value} />
    </div>)
  }
}

class Children1 extends Component {
  state = {
    value: 'Children1传递的数据'
  }
  sonHandeValue = () => {
    this.props.sonHandeValue(this.state.value)
  }
  render () {
    return (<div><button onClick={this.sonHandeValue}>1--->2 传值</button></div>)
  }
}

class Children2 extends Component {

  render () {
    return (<div>组件二:{this.props.value}</div>)
  }
}

props校验

在props传递中,对于组件来说,无法保证接收到的props时什么格式的数据,react为了保证数据类型的准确性,推荐我们使用prop-types来进行校验props类型,也可以添加props默认值

  1. 下载prop-type插件
 npm install --save prop-types

2.在项目中使用,以下代码列出了常用的校验类型,详情配置可看官方文档

import { Component } from "react";
// 1. 导入prop-types
import PropTypes from 'prop-types';

export class Father extends Component {
  state = {
    str: '',
    arr: [],
    bool: true,
    num: 0,
    obj: {},
    func: () => { }
  }

  render () {
    return (<div><TypeProps {...this.state} /></div>)
  }
}

class TypeProps extends Component {
  render () {
    return (<div>TypeProps{this.props.defaultvalue}</div>)
  }
}
/**
 * TypeProps:需要添加校验的组件
 * propTypes:校验类型
 */
TypeProps.propTypes = {
  str: PropTypes.string.isRequired, // 校验字符串并且必填
  arr: PropTypes.array, // 校验数组
  bool: PropTypes.bool, // 校验布尔值
  func: PropTypes.func, // 校验函数
  num: PropTypes.number, // 校验数值
  obj: PropTypes.object, // 校验对象
}

/**
 * TypeProps:需要添加校验的组件
 * defaultProps:默认值
 */
TypeProps.defaultProps = {
  defaultvalue: '默认值'
}

受控组件

受控组件一般是HTML中可改变自己状态的元素,一般有表单类的元素,因为这些元素自身可改变状态,所以需要让React中的state保持同步,使用this.setstate来改变state的状态,当我们给表单绑定value的时候,必须要给表单同步绑定change事件,不然则会有如下报错:

image.png

import { Component } from "react";
class ControlledInput extends Component {
  state = {
    value: "你好,React",
  };
  changeValue = (e) => {
    const { value } = e.target;
    this.setState({
      value,
    });
  };
  render() {
    return (
      <div>
        <h1>表单受控组件</h1>
        <input
          type="text"
          value={this.state.value}
          onChange={this.changeValue}
        />
      </div>
    );
  }
}
export default ControlledInput;

ref

使用createRef来创建ref对象,ref可以用来获取dom对象或者是组件,使用current来指定dom

import { Component, createRef } from "react";

class MyRef extends Component {
  constructor() {
    super();
    // 创建ref对象
    this.divRef = createRef();
  }
  handleValue() {
    // 使用current来指定dom
    this.divRef.current.innerHTML = "更改后的值";
  }
  render() {
    return (
      <div>
        {/* 绑定ref */}
        <span ref={this.divRef}>你好,ref</span>
        <button onClick={() => this.handleValue()}>点击更改文字</button>
      </div>
    );
  }
}

export default MyRef;

Context

当我们需要组件多层传递的时候,一层一层的传递容易造成数据丢失或者是混淆,书写起来也比较麻烦,这时候就可以用到Context,在父级组件中提供数据,子孙组件中引用数据即可,类似于Vue里面的依赖注入provideinject

image.png

image.png

  1. 从react中导入createContext()
  2. 从createContext()里面结构出Provider和Consumer
  3. 用Provider包裹提供数据的父组件,使用vlaue属性传递需要提供的数据
  4. 用Consuerm包裹使用数据的子组件
import "./03-context.css";
import { Component, createContext } from "react";
const { Provider, Consumer } = createContext();
//提供数据的父组件
class Node extends Component {
  state = {
    value: "提供给子组件的值",
  };
  render() {
    return (
      <Provider value={this.state.value}>
        <div className="Node">
          Node:
          <SubNode />
        </div>
      </Provider>
    );
  }
}

class SubNode extends Component {
  render() {
    return (
      <div className="SubNode">
        SubNode:
        <Child />
      </div>
    );
  }
}

class Child extends Component {
  render() {
    return (
      <div className="Child">
        Child:<Consumer>{(value) => <span>{value}</span>}</Consumer>
      </div>
    );
  }
}

export default Node;

生命周期

每个组件都有自己的生命周期,简单来说就是一个组件在挂载到销毁的过程,就算是一个生命周期

React生命周期图解

  • 创建时(挂载阶段):constructor ---> render ---> componentDidMount
  • 更新时(更新阶段):render ---> componentDidUpdate
  • 卸载时(卸载阶段):componentWillUnmount

在componentDidUpdate中使用setState时需要退出条件,不然会导致死循环

钩子函数触发时机作用
constructor创建组件时,最先执行初始化state
render组件渲染时触发渲染视图在render中不能调用setState()
componentDidMount组件完成挂载后实现DOM操作,发送请求
componentDidUpdate组件更新,完成DOM渲染后实现DOM操作,发送请求
componentWillUnmount组件卸载清除定时器等操作
forceUpdate调用forceUpdate()时触发强制更新

组件复用

当我们在实现业务的时候经常会遇到实现的逻辑时一样的,但是渲染的ui不同,如果重复复制组件代码进行实现的话,那代码中重复的代码太多,以后就不方便维护,这时候react给我们推出了render props模式和高阶组件来进行代码的复用,下面分别使用了render props模式和高阶组件来复用组件

render prop

render props指一种在 React 组件之间使用一个值为函数的 prop 共享代码的简单技术,具有 render prop 的组件接受一个返回 React 元素的函数,并在组件内部通过调用此函数来实现自己的渲染逻辑,当然官网上说明了render prop 是因为模式才被称为 render prop,任何*被用于告知组件需要渲染什么内容的函数 prop 在技术上都可以被称为 render prop,也可以使用其他方法名代替,推荐使用children

跟官网上的案例大致相同,跟随鼠标的凹凸曼打🐤,如果想看使用children模式的可以点击官网查看,只需要将render换成children,接收是放在组件内部渲染就行。

image.png

import { Component } from "react";

export class RenderProp extends Component {
  render () {
    return (
      <div>
        <Mouse render={data => (<div>x:{data.x} y:{data.y}</div>)} />
        <Mouse render={data => (<img src="https://img0.baidu.com/it/u=2311657326,1601596104&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501" alt="" style={{
          position: 'absolute',
          top: data.y,
          left: data.x,
          width: 200,
          height: 200,
          transform: 'translate(-50%,-50%)'
        }}></img>)
        } />
      </div >
    )
  }
}

class Mouse extends Component {
  state = {
    x: 0,
    y: 0
  }
  // 设置鼠标移动值
  handleMouse = (e) => {
    this.setState({
      x: e.clientX,
      y: e.clientY
    })
  }
  // 绑定鼠标移动事件
  componentDidMount () {
    window.addEventListener('mousemove', this.handleMouse)
  }
  // 解绑鼠标移动事件
  componentWillUnmount () {
    window.removeEventListener('mousemove', this.handleMouse)
  }
  render () {
    // 这里return 的 render 组件使用的时候必须有个render方法,当然不一定非要叫render,方法名由自己定义,接收时保持一致就行
    return this.props.render(this.state)
  }
}

高阶组件

import { Component } from "react";

// 暴露的组件
export class HigtComponent extends Component {
  render () {
    return (<div>
      <MousePosition />
      <MouseCat />
    </div>)
  }
}

// 高阶函数
function WithMouse (MousePosition) {
  class Mouse extends Component {
    state = {
      x: 0,
      y: 0
    }
    handerMouse = (e) => {
      this.setState({
        x: e.clientX,
        y: e.clientY
      })
    }
    componentDidMount () {
      window.addEventListener('mousemove', this.handerMouse)
    }
    componentWillUnmount () {
      window.removeEventListener('mousemove', this.handerMouse)
    }
    render () {
      return (<MousePosition {...this.state} />)
    }
  }
  return Mouse
}
// 提供视图的组件
const Position = props => {
  return (<div>x:{props.x} y:{props.y}</div>)
}
const Cat = props => {
  return (<img src="https://img0.baidu.com/it/u=2311657326,1601596104&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=501" alt="" style={{
    position: 'absolute',
    top: props.y,
    left: props.x,
    width: 200,
    height: 200,
    transform: 'translate(-50%,-50%)'
  }}></img>)
}
// 用高阶组件进行封装
const MousePosition = WithMouse(Position)
const MouseCat = WithMouse(Cat)