react语法

记录下react的基本语法及遇到的问题,分享给大家。让分享成为创作的动力!
react脚手架安装,组件定义,组件传值方式
学习文档链接 React中文官网
demo:https://gitee.com/wanglun945/project-react.git

一、使用

index.js

打包入口文件-- index.js 中使用root.render 渲染 引入的app.js。

app.js

通过import引入 image, scss,component.

import logo from './logo.svg';
import './App.css';
import Clock from "./component/clock"
import Profile from './component/profile';
import {useState} from "react";
function App() {
    let [liStore, setLiStore] = useState([]);
  let compName = {name: "zs"};
  let changeName = name => {
    compName = {name};
  }
  return (
    <div className="App">
      <section>
        <Clock compName={compName} onChangeName={changeName}/>
        <Profile liStore={liStore} onAdd={setLiStore}></Profile>
        <img src={logo} alt="" srcset="" />
      </section>
    </div>
  );
}
export default App;

组件

函数式组件 (推荐使用)

用来定义无状态、无副作用的UI组件,以函数的形式返回一个React元素。函数式组件通常使用函数参数来接收输入数据(props),并返回渲染的结果。易于编写和理解,并且在性能方面也有一定的优势。Class组件的语法相对复杂,代码量较多,而且在处理状态和生命周期时可能存在一些问题(如难以复用状态逻辑、容易产生性能问题等)

export default function Profile ({liStore, onAdd}) {
  let onAddLi = () => {
    let newL = [...liStore, {name: "11"}]
    onAdd(newL)
  }
  return (
    <>
      <button onClick={() => onAddLi()}>+</button>
      <ul>
        {liStore.map((v, i) => {
          return (<li style={{
            lineHeight: "20px",
            color: "#409fee"
          }} key={i}>{i % 2 === 0 ? v.name : "66"}</li>)
        })}
      </ul>
    </>
  )
}
Class组件

拥有自身的状态(通过this.state来定义)和生命周期方法(如componentDidMount()、componentDidUpdate(), componentDidUpdate()、render()),可以处理用户交互和其他复杂的逻辑。

import React from "react";
class CompName extends React.Component {
  constructor (props) {
    super(props);
    this.state = {}
  }
  shouldComponentUpdate (nextProps, nextState) {
    console.log(nextProps, nextState);
    return true;
  }
  componentDidMount () {
	console.log("组件挂载")
  }
  render () {
    return (
      <>
        <div>{this.props.name}</div>
        <div>{this.props.age}</div>
      </>
    )
  }
}

组件通讯

props

父组件通过 props 将数据传递给子组件。在父组件中定义属性并将其传递给子组件,子组件可以通过 this.props 来获取这些值。

eg:
父组件:

import Calculator from "./component/Calculator"
import BoilingVerdict from "./component/BoilingVerdict"
import { useState } from "react";

export default function Home () {
  let [temperature, setTemperature] = useState(0)
  let emitTemp = value => {
    setTemperature(value)
  }
  return (
    <>
      <Calculator onChangeTemp={emitTemp} />
      <BoilingVerdict temperature={temperature}  />
    </>
  )
}

函数式组件
函数的第一个形参可接受父组件传递进来的属性,方法

export default function BoilingVerdict (props) {
  if (props.temperature > 100) {
    return (
      <div>水沸腾了</div>
    )
  }
  return (
    <div>当前水温{props.temperature}</div>
  )
}

class组件
子组件中使用super关键字。它用于继承父组件的属性方法。

import {Component} from "react";
export default class Calculator extends Component {
  constructor (props) {
    super(props);
    this.state = {
      temperature: 0
    };
    this.handleChangeValue = this.handleChangeValue.bind(this);
  }
  handleChangeValue (e) {
    this.props.onChangeTemp(e.target.value);
    this.setState({
      temperature: e.target.value
    })
  }
  render () {
    return (
      <>
        <input value={this.state.temperature} onChange={this.handleChangeValue} />
      </>
    )
  }
}

context

注意class组件需设置 contextType: 挂载在 class 上的 contextType 属性可以赋值为由 React.createContext() 创建的 Context 对象。此属性可以让你使用 this.context 来获取最近 Context 上的值。
1、context.js 文件,用于存放context数据

export const ThemesCtx = React.createContext(Themes.dark);
export const Themes = {
  light: {
    background: "#409fee",
  },
  dark: {
    background: "#000",
  }
}

2、父辈组件
ThemesCtx.Provider 提供的value使用state中的数据,防止Provider 重渲染子组件更新。

import F from "./f";
import Btn from "./btn"
import { ThemesCtx, Themes } from "../context/theme-ctx"; // 引入context
import React from "react";
function Comp () {
  return (
    <>
      <span>custom</span>
    </>
  )
}
export default class P extends React.Component {
  static contextType = ThemesCtx; 
  constructor (props) {
    super(props);
    this.state = {
      theme: Themes.light
    }
    this.changeTheme = this.changeTheme.bind(this);
  }
  changeTheme () {
    this.setState({
      theme: Themes.dark
    })
  }
  render () {
    return (
      <>
        <ThemesCtx.Provider value={this.state.theme}>
          <F>
            <Btn handleChangeTheme={this.changeTheme} />
          </F>
        </ThemesCtx.Provider>
      </>
    )
  }
}

孙级组件- class组件

import React from "react";
import { ThemesCtx } from "../context/theme-ctx";
export default class S extends React.Component {
  static contextType = ThemesCtx;
  render () {
    return (
      <div>
        子级 {this.context.background}
      </div>
    )
  }
}

子级组件- 函数式组件

  1. 使用 ThemesCtx.Consumer 组件获取context。
  2. 使用 useContext
import S from "./s";
import { ThemesCtx } from "../context/theme-ctx";
export default function F () {
  const theme = useContext(ThemesCtx);
  return (
    <>
      <ThemesCtx.Consumer>
        {
          val => (
            <div>父集背景色: {val.background}</div>
          )
        }
      </ThemesCtx.Consumer>
      <div>useContext: {theme.background}</div>
      <S />
    </>
  )
}
refs

用 React.forwardRef API 将 refs 转发到目标组件;接受一个渲染函数,其接收 props 和 ref 参数并返回一个 React 节点。目标组件接收ref,并设置ref属性值即可。外部组件使用 var targetRef = React.useRef() ;targetRef .current 可获取指向的DOM 节点。如果需要使用目标组件中的属性和方法,使用useImperativeHandle暴露对应的属性和方法接口,具体用法如下
父级组件

import React from "react";
import FancyBtn from "./photos/fancyBtn";
export default function Photos () {
  const buttonRef = React.createRef();
  let handlerClick = () => {
    buttonRef.current.focus();
  }
  return <>
    <FancyBtn ref={buttonRef} handlerClick={handlerClick}>Click</FancyBtn>
  </>
}

目标组件

import React, { useImperativeHandle } from "react";
const FancyBtn = React.forwardRef((props, ref) => {
  const inputRef = React.createRef(null);
  let focusFn = () => {
    inputRef.current.focus();
  }
  useImperativeHandle(ref, () => ({ // 暴露focus方法
    focus: focusFn
  }))
  return (
    <>
      <button ref={ref} onClick={props.handlerClick}>
        {props.children}
      </button>
      <input ref={inputRef} />
    </>
  )
})
export default FancyBtn

组件嵌套

使用children,可以是任何类型的内容,包括文本、元素、组件等
使用props传递自定义方法。
以下以class组件为例,函数式组件参照props的使用方式
1.父组件

import F from "./f";
import Btn from "./btn"
import React from "react";
function Comp () {
  return (
    <>
      <span>custom</span>
    </>
  )
}
export default class P extends React.Component {
  render () {
    return (
      <>
          <F customComp={Comp}>
            <Btn handleChangeTheme={this.changeTheme} />
          </F>
      </>
    )
  }
}
  1. 子组件

export default function F ({children, customComp}) {
  return (
    <>
      <div>子组件:{children}</div>
      <div>自定义组件:{customComp()}</div>
    </>
  )
}

受控组件和非受控组件

受控组件和非受控组件是两种处理表单元素的方式。下面是它们的简要说明:

受控组件(Controlled Components):受控组件是由React控制并管理其值的表单元素。通过使用state和事件处理函数,React可以实时更新和同步表单元素的值。当用户输入数据时,值会存储在React组件的状态中,并通过事件处理函数进行更新。这种方式提供了精确的控制和验证,适用于需要实时响应和验证用户输入的场景。
例如,一个受控的文本输入框的示例代码如下:


class MyForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: ''
    };
  }

  handleChange(event) {
    this.setState({ value: event.target.value });
  }

  handleSubmit(event) {
    event.preventDefault();
    // 执行表单提交操作
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input
          type="text"
          value={this.state.value}
          onChange={this.handleChange}
        />
        <button type="submit">提交</button>
      </form>
    );
  }
}

非受控组件(Uncontrolled Components):非受控组件是由DOM元素本身保存和更新其值的表单元素。React中的非受控组件通过使用ref来访问DOM元素,并直接操作其值。与受控组件相比,非受控组件更加简单,适用于一些简单的表单场景。
以下是一个非受控的文本输入框的示例代码:

class MyForm extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  handleSubmit(event) {
    event.preventDefault();
    // 使用this.inputRef.current.value来获取输入框的值
    // 执行表单提交操作
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <input type="text" ref={this.inputRef} />
        <button type="submit">提交</button>
      </form>
    );
  }
}

总结来说,受控组件需要在React组件内部管理其值,而非受控组件则由DOM元素自己管理其值。选择使用哪种方式取决于具体的需求和场景。

Hook

Hook 是一些可以让你在函数组件里“钩入” React state 及生命周期等特性的函数。Hook 不能在 class 组件中使用 —— 这使得你不使用 class 也能使用 React。

规则
  • 只在最顶层使用 Hook( React 靠的是 Hook 调用的顺序。Hook 的调用顺序在每次渲染中都是相同的,所以它能够正常工作)
  • 只在 React 函数中调用 Hook
常用api
1. useState

允许在 React 函数组件中添加 state 的 Hook

2. useEffect

跟 class 组件中的 componentDidMount、componentDidUpdate 和 componentWillUnmount 具有相同的用途

//	每次我们重新渲染,都会生成新的 effect;
//  React 会在执行当前 effect 之前对上一个 effect 进行清除;
// 第二个参数是个数组,如果某些特定值在两次重渲染之间没有发生变化,可以通知 React 跳过对 effect 的调用;
// 确保数组中包含了所有外部作用域中会随时间变化并且在 effect 中使用的变量
  useEffect(() => { 
    console.log(666); // componentDidMount 时执行。
    return () => {
      console.log(555); // 初始化时和componentDidUpdate 先执行 555 再执行 666 , componentWillUnmount 时执行。
    }
  }, [])
3. useReducer

state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化

import { useReducer } from "react";
// 它接收一个形如 (state, action) => newState 的 reducer,并返回当前的 state 以及与其配套的 dispatch 方法。
export default function UseR ({initCount, initName}) {
  const init = () => {
    return {count: initCount, name: initName}
  }
  function reducer (state, action) {
    switch (action.type) {
      case "add":
        return {count: state.count + 1, name: "add"};
      case "minus":
        return {count: state.count - 1, name: "minus"};
      case "reset":
        return init({initCount: action.initCount, initName: action.initName});
      default:
        throw new Error("**")
    }
  }
  const [state, dispatch] = useReducer(reducer, {initCount, initName}, init);
  return (
    <>
      <div>count: {state.count}</div>
      <div>name: {state.name}</div>
      <div>
        <button onClick={() => {dispatch({type: "add"})}}>+1</button>
      </div>
      <div>
        <button onClick={() => {dispatch({type: "minus"})}}>-1</button>
      </div>
      <div>
        <button onClick={() => {dispatch({type: "reset", initCount, initName})}}>reset</button>
      </div>
      <div>
        <button onClick={() => {dispatch({type: "1"})}}>Error</button>
      </div>
    </>
  )
}
4. useCallback

用于缓存回调函数以提高性能。它接受两个参数:回调函数和依赖项数组。
当依赖项数组中的值发生变化时,React将重新创建回调函数。如果依赖项数组为空,则回调函数只会在组件首次渲染时创建一次。使用useCallback的常见场景是在将回调函数传递给子组件时,防止不必要的重新渲染。

import React, { useCallback } from 'react';
function MyComponent() {
  const handleClick = useCallback(() => {
    console.log('Button clicked!');
  }, []);
  return (
    <button onClick={handleClick}>Click me</button>
  );
}

在上面的例子中,handleClick回调函数将始终是相同的引用。只有当MyComponent组件首次渲染时,该函数才会被创建。这可以避免在每次重渲染时创建新的回调函数,并且可以提高性能。请注意,useCallback只在性能优化方面有用,通常在处理有性能问题的组件时使用它。如果组件没有性能问题,可以不使用useCallback。

5. useMemo

用于缓存计算结果以提高性能。它接受两个参数:计算函数和依赖项数组。
当依赖项数组中的值发生变化时,React将重新计算和缓存计算函数的结果。如果依赖项数组为空,则计算函数只会在组件首次渲染时执行一次。使用useMemo的常见场景是在渲染过程中执行昂贵的计算,并且这些计算的结果在依赖项未改变时保持不变。
下面是一个示例:

import React, { useMemo } from 'react';
function MyComponent() {
  const expensiveResult = useMemo(() => {
    // 执行昂贵的计算
    console.log('Expensive computation');
    return 2 + 2;
  }, []);
  return (
    <div>{expensiveResult}</div>
  );
}

在上面的例子中,expensiveResult的值将在组件首次渲染时计算,并被缓存起来。在后续的重渲染中,由于依赖项数组为空,计算函数将不再执行,而是直接返回缓存的结果。这样可以避免在每次重渲染时重复执行昂贵的计算,从而提高性能。请注意,useMemo应该谨慎使用,只在性能优化方面有需要时才使用。如果没有性能问题,可以不使用useMemo。此外,如果计算函数非常简单,那么使用useMemo可能会产生反效果,因为执行计算函数的开销可能大于直接计算结果的开销。

6. useRef

一个用于获取和操作 DOM 元素或其他可变值的 Hook。它的主要用法有两种:
(1)获取 DOM 元素的引用:通过 useRef 创建一个 ref 对象,然后将该 ref 对象赋值给组件中的某个 DOM 元素的 ref 属性。这样就可以通过 ref.current 来访问该 DOM 元素,以执行一些操作,比如获取或修改元素的属性、样式等。
示例代码如下:

import React, { useRef } from 'react';
function MyComponent() {
  const myRef = useRef();
  const handleClick = () => {
    console.log(myRef.current); // 访问 DOM 元素
    myRef.current.style.backgroundColor = 'red'; // 修改元素样式
  };
  return (
    <div>
      <button ref={myRef} onClick={handleClick}>
        Click me
      </button>
    </div>
  );
}

(2)存储可变值:useRef 也可以用于存储组件中的可变值,并且该值的修改不会触发组件重新渲染。这在某些情况下非常有用,比如在计时器、数据缓存等场景中。

import { useRef, useState, useCallback } from 'react';
export function Father () {
  const [height, setHeight] = useState(0);
  let sonRef = useCallback(node => {
    if (node != null) {
      let h = $(node).height();
      setHeight(h)
    }
  }, [])
  return (
    <>
      <Son sonRef={sonRef}></Son>
      {height > 0 && <h2>h1 的高度 {height}</h2>}
    </>
  )
}

function Son ({sonRef}) {
  let [show, setShow] = useState(false);
  let handlerClick = () => {
    setShow(true)
  }
  if (!show) {
    return (
      <button onClick={handlerClick}>显示H1标签</button>
    )
  }
  return (
    <h1 ref={sonRef}>h1</h1>
  )
}

二、安装

检查本级是否安装node.js,没有安装的下载一个nvm,node包管理工具。不知道的小伙伴可以点击这里
执行命令 npm i create-react-app
在这里插入图片描述
执行命令 create-react-app hello-react
在这里插入图片描述
执行npm run start,项目运行起来后开始编写hellow react。对于spa应用来说,通过虚拟DOM树挂载在root节点上。ReactDOM树挂载在root节点下。ReactDOMRoot.js中提供createRoot()方法来创建ReactDOM树的Root节点,并且 render()方法使其渲染或更新。
在这里插入图片描述

三、遇到问题

  1. Q: 初始化加载组件会渲染两次的问题;
    A: 使用脚手架创建项目,默认会开启严格模式,在严格模式下,开发环境下会刻意执行两次渲染,用于突出显示潜在问题。在index.js 中注释 React.StrictMode 即可。
    ps: 严格模式会检测过时的生命周期方法;检测不安全的生命周期方法;检测废弃的 context API 使用;检测副作用等

  2. Q: 父组件修改props值,子组件不更新视图;
    A: 在函数式组件中传递给子组件的值,用父组件state的值,使用setState修改,触发render方法更新视图。

  3. Q: class组件声明了,而且使用了,但是终端提示 ‘logProps’ is defined but never used no-unused-vars。如图所示
    A: 检查声明组件的变量名 首字母是否大写,改为大写即可。
    在这里插入图片描述

  4. Q: 使用 jquery 报错 Line 15:17: ‘$’ is not defined no-undef
    A: 下载jquery包,再引入即可。 先执行 npm i jquery -D , 再在使用的文件中引入 import $ from “jquery”
    在这里插入图片描述

  5. Q: 使用sass报错
    A: 下载 npm i sass sass-loader node-sass -D后,再执行npm run start 即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值