会社から React の知識が求められると、Vueer は学ばなければなりません

序文

Vue のヘビー ユーザーである私は、React の使い方を学ぶときにどうしても少し不快になり、さらには少し焦ります。

しかし、現状は変わってきており、今 React を学ばないと仕事を見つけるのは非常に困難です。

したがって、たとえ React を学ぶのが難しくても、それを使いこなすには習熟する必要があります。

結局のところ、プログラミングとはこういうもので、言語によっては理解しにくい場合もありますが、フレームワーク開発者ではないので、それに適応するしかありません。

イデオロギー

React を学習するときは、まず Vue のことを忘れてください。

焦らず、ゆっくり時間をかけてください。

文章

理解できない場合は読み飛ばして、理解できるところから吸収してください。

HTML文字列をレンダリングする

function Test() {
  return (
    <>
      <p dangerouslySetInnerHTML={
   
   { __html: '<i style="color:red;">123</i>' }} />
    </>
  )
}
复制代码

条件付きレンダリング、リストレンダリング

v-if および v-for 命令を持つ Vue とは異なり、React は js を直接使用します。

React は配列を自動的に展開してレンダリングします。

export function Login(props: any) {

  const [show, setShow] = useState(false)
  const [list, setList] = useState(['小梅', '小军', '小强'])

  return (
    <>
      {show ? '显示的文本' : '隐藏的文本'}
      <br />
      {list}
    </>
  );
}

复制代码

イベント

伝達関数。以下の例では、父组件ボタンをクリックした場合とTest组件ボタンをクリックした場合の両方が出力されます点我了

パラメータを渡す必要がある場合は、アロー関数を使用してください。

これは親コンポーネントです。

export function Login(props: any) {
  function handleClick() {
    console.log('点我了');
  }
  return (
    <>
      <button onClick={handleClick}>父组件</button>
      <Test xx={handleClick} />
    </>
  );
}
复制代码

これは子コンポーネントです。

export function Test(props: any) {
  return <button onClick={props.xx}>Test组件</button>
}
复制代码

制御されたコンポーネント

state を使用して、フォームの入力表示を制御します。

class MyComponent extends React.Component{
  state = {
    inputVal: 'input'
  }
  handleInput = e => {
    this.setState({
      inputVal: e.target.value
    })
  }
  render() {
    return (
      <>
        <p>{this.state.inputVal}</p>
        <input type="text" value={this.state.inputVal} onInput={this.handleInput} />
      </>
    )
  }
}
复制代码

制御されていないコンポーネント

フォームの入力表示は状態によって制御されません。代わりに、フォーム要素の DOM を取得して、その値を取得または設定します。

import { useRef } from "react";

export function Login(props: any) {
  const nameInput = useRef<HTMLInputElement>(null)

  function handleClick() {
    console.log(nameInput.current?.value);
  }
  return (
    <>
      姓名:
      <input ref={nameInput} type="text" />
      <br />
      <button onClick={handleClick}>提交</button>
    </>
  );
}
复制代码

親子コンポーネントの通信

親は prop を通じて子にデータを渡します。子が親にデータを渡すとき、子は関数も渡し、子はその関数を呼び出して親のデータを渡して変更します。

親コンポーネント

// 父组件
class MyComponent extends React.Component{
  state = {
    count: 99
  }
  render() {
    return (
      <div>
        <ChildComponent count={this.state.count} changeCount={this.changeCount} />
      </div>
    )
  }
  // 接收子组件传递的count来修改自身的count
  changeCount = count => {
    this.setState({
      count
    })
  }
}
复制代码

サブアセンブリ

// 子组件
class ChildComponent extends React.Component {
    render() {
        return <div>
          {this.props.count}
          <button onClick={this.addCount}>addCount</button>
        </div>
    }
    addCount = () => {
        // 获取父组件传递的props
        const { changeCount, count } = this.props
        // 调用父组件的changeCount方法
        changeCount(count + 1)
    }
}
复制代码

スロットを実装する

React は Vue のようなデフォルト スロット、名前付きスロット、スコープ スロットを実装しており、実際にはすべてを渡すことができるという React の prop 機能を利用しています。

デフォルトのスロット、this.prop.children を使用

export class Login extends React.Component {
  render() {
    return (
      <>
        <Child>我是光</Child>
      </>
    )
  }
}

function Child(props: any) {
  return (
    <div>
      <div>我显示父组件传进来的内容:</div>
        <br />
        {props.children}
    </div>
  )
}
复制代码

名前付きスロット。親コンポーネントは必ずしも関数を渡す必要はなく、ラベルを直接レンダリングして、子コンポーネントがそれを受け取ることもできます。

export class Login extends React.Component {
  render() {
    return (
      <>
        <Child renderTitle={
          <div>这。。。</div>
        }>我是光</Child>
      </>
    )
  }
}

function Child(props: any) {
  return (
    <div>
      {props.renderTitle ? props.renderTitle : <div>默认插槽</div>}
    </div>
  )
}
复制代码

スコープスロット。子コンポーネントは関数パラメータを使用して、親コンポーネントにデータを取り込むことができます。

  render() {
    return (
      <>
        <Child renderItem={(item: string, index: number) => {
          return <li>{index+1}号,{item}</li>
        }}></Child>
      </>
    )
  }
}

function Child(props: any) {
  const arr = [
    '姚明',
    '科比'
  ]
  return (
    <div>
      {arr.map((item, index) => {
        return props.renderItem ? props.renderItem(item, index) : <div>空</div>
      })}
    </div>
  )
}
复制代码

setState

setState を使用する場合は、元のデータを直接変更しないように注意してください。深いレベルのオブジェクトを変更する必要がある場合は、便利なようにimmerライブラリを使用できます  。(より人気のあるもの)

this.setState({
    userName: '小花',
    userAge: 19
})
复制代码

ライフサイクル

React で一般的に使用されるライフサイクル関数には、componentDidMount と shouldComponentUpdate があります。ComponentDidMount は、Vue のマウントされたフックに似ています。shouldComponentUpdate を使用すると、ユーザーはコンポーネントをレンダリングする必要があるかどうかを決定できます。

class MyComponent extends React.Component{
  constructor(props) {
    super(props)   
  }
  shouldComponentUpdate (nextProps, nextState, nextContext) {
    console.log(nextState.count, this.state.count)
    if (nextState.count !== this.state.count) {
      return true // 允许渲染,会往下执行render
    }
    return false // 不渲染,不执行render
  }
  render() {
    return ...
  }
}
复制代码

反応ポータル

親要素が overflow: hidden を設定すると、子要素は親要素の境界を越えて表示されなくなります。これはポップアップ コンポーネントにはあまり適していません。

たとえば、親コンポーネントは次のようになります。

export function Login() {
  const [show, setShow] = useState(false)
  function handleClick() {
    setShow(!show)
  }
  return (
    <div className='father'>
      <Model show={show} />
      <button onClick={handleClick}>打开弹框</button>
    </div>
  );
}
复制代码
.father {
  overflow: hidden;
  position: fixed;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 250px;
  height: 250px;
  border: 1px solid #000;
}
复制代码

次に、サブコンポーネント:

function Test(props: any) {
  return (
    <>
      {
        props.show ? 
        <div className='model'>
          你好,我是弹框。
        </div> :
        <div>啥也不是</div>
      }
    </>
  )
}
复制代码
.model {
  position: fixed;
  left: 50%;
  top: 50%;
  /* transform: translate(-50%, -50%); */
  background-color: red;
  color: #fff;
  width: 250px;
  height: 250px;
  border: 1px solid #000;
}
复制代码

こうなるでしょう。

サブコンポーネントをポータルに変更すると、問題はすぐに解決されました。

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

function Test(props: any) {
  return (
    <>
      {
        props.show ? 
        ReactDOM.createPortal(<div className='model'>你好,我是弹框。</div>, document.body)
        :
        <div>啥也不是</div>
      }
    </>
  )
}

export default React.memo(Test)
复制代码

反応コンテキスト

VueのProvide/Injectと同じ機能ですが、書くことが少し増えます。

import { createContext,useContext } from "react";

const ThemeContext = createContext('');

export function Login() {
  
  return (
    <ThemeContext.Provider value='喔喔喔'>
      <div className='father'>
        <Test />
      </div>
    </ThemeContext.Provider>
  );
}

function Test(props: any) {
  const theme = useContext(ThemeContext);
  return (
    <>
      <div>{theme}</div>
    </>
  )
}
复制代码

非同期コンポーネント

使用状況を記録するだけです。

import React from "react";

export function Login() {
  return (
    <div className='father'>
      <React.Suspense fallback={<div>loading...</div>}>
        <Lazyy />
      </React.Suspense>
    </div>
  );
}

const Lazyy = React.lazy(() => new Promise((resolve) => {
  setTimeout(() => {
    const a = import('../../components/test')
    // @ts-ignore
    resolve(a)
  }, 1000);
}))

复制代码

useEffect

正直、この useEffect について当時は全く理解できませんでした。ただし、React の機能コンポーネントのレンダリング ロジックを理解していれば、それほど混乱することはありません。

React の機能コンポーネントはクラスコンポーネントとは異なり、機能コンポーネント内でメソッドを定義すると、再レンダリングされるたびに新しいメソッドが定義されるため、以前のメソッドの参照とは異なります。

メソッドがクラス コンポーネントで定義されている場合、その参照は変更されず、再レンダリングでは render メソッドが再実行されるだけです。

React を学習していて useEffect について混乱している場合は、Dan が書いたこの記事を必ず読んでください。useEffect 完全ガイド

import {  useState } from "react";

// let a = 1

export function Login(props: any) {
  const [count, setCount] = useState(0); 

  function say () {
    let a = 1
    console.log('Hello Vue and React, are you ok ?', a ++);
  }
  say()

  return (
    <>
      <button onClick={() => setCount(count + 1)}>加1,count:{count}</button>
    </>
  );
}
复制代码

useState、useMemo、useCallback、React.memo

ここで説明したいのは、React が状態を変更するたびに、機能コンポーネントが再実行され、そのサブコンポーネントも再実行されるということです。

サブコンポーネントを再実行したくない場合は、React.memo を使用してサブコンポーネントをラップします。Object.is を使用して、前後の props が同じかどうかを比較します。異なる場合は、サブコンポーネント再レンダリングされます。

ここでは、Test コンポーネントを使用する Login (名前は重要ではありません、jy) というコンポーネントを作成しました。

import { useCallback, useMemo, useState } from "react";
import Test from "components/test";

export function Login(props: any) {
  const [count, setCount] = useState(0); 

  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, [count]);

  // Test 组件执行一遍
  const data = useMemo(() => ({
    lalala: count
  }), [])

  // Test 组件首次会执行一遍,之后每次 count 加1,Test 组件都会执行一遍
  // const data = useMemo(() => ({
  //   lalala: count
  // }), [count])

  // Test 组件首次会执行一遍,在 count 为3或4的时候会执行一遍
  // const data = useMemo(() => ({
  //   lalala: count
  // }), [count === 3])

  return (
    <>
      <button onClick={() => handleClick()}>加1,count:{count}</button>
      <Test suibian={data}   />
    </>
  );
}
复制代码

テストコンポーネントは次のとおりです

import React  from 'react';

function Test(props: any) {
  console.log('Test函数组件执行了一遍');

  return <button>Test</button>
}

export default React.memo(Test)
复制代码

useコールバック

現時点では、ログイン コンポーネントのみがあります (名前は気にしないでください、jym)。

なぜ次のような状況が起こるのでしょうか?

最初のケースでは、依存関係は空の配列であり、渡された関数は useCallback によって保存され、更新されることはありません。また、クロージャーがあるため、渡された関数で取得されるカウント値は常に 0 になります。したがって、ボタンがクリックされるたびに setCount に渡される値は、実際には 1 です。

2 番目のケースでは、カウントの依存関係が渡されます。カウントが変更された後、useCallback 内の関数も再定義され、今回の Login 関数の実行時に定義されたカウント値が取得されます。このときのカウント値が最新の値となる。

import { useCallback, useState } from "react";

export function Login(props: any) {
  const [count, setCount] = useState(0); 

  // 点击按钮后,count 加1,之后永远都为1了。
  const handleClick = useCallback(() => {
    setCount(count + 1);
  }, []);

  // 每次点击按钮,count 都会加1
  // const handleClick = useCallback(() => {
  //   setCount(count + 1);
  // }, [count]);

  return (
    <>
      <button onClick={() => handleClick()}>加1,count:{count}</button>
    </>
  );
}
复制代码

React は計算されたプロパティを実装します

クラスのゲッターを使用して計算されたプロパティを実装しますが、キャッシュは実装できません。

class Example extends Component {
  state = {
    firstName: '',
    lastName: '',
  };

  // 通过getter而不是函数形式,减少变量
  get fullName() {
    const { firstName, lastName } = this.state;
    return `${firstName} ${lastName}`;
  }

  render() {
    return <Fragment>{this.fullName}</Fragment>;
  }
}
复制代码

キャッシュを実装したい場合は、memoize-one ライブラリを使用できます。これは本質的に、前後に渡されたパラメータを比較する高階関数です。前回渡されたパラメータと同じ場合、最後にキャッシュされた結果が返されます。それ以外の場合、戻り値は新しい入力パラメータに基づいて再計算されます。

import memoize from 'memoize-one';
import React, { Fragment, Component } from 'react';

class Example extends Component {
  state = {
    firstName: '',
    lastName: '',
  };

  // 如果和上次参数一样,`memoize-one` 会重复使用上一次的值。
  getFullName = memoize((firstName, lastName) => `${firstName} ${lastName}`);

  get fullName() {
    return this.getFullName(this.state.firstName, this.state.lastName);
  }

  render() {
    return <Fragment>{this.fullName}</Fragment>;
  }
}
复制代码

React Hooks を使用して計算プロパティを実装するにはどうすればよいですか? useMemo フックを使用します。

import React, { useState, useMemo } from 'react';

function Example(props) {
  const [firstName, setFirstName] = useState('');
  const [lastName, setLastName] = useState('');
  // 使用 useMemo 函数缓存计算过程
  const renderFullName = useMemo(() => `${firstName} ${lastName}`, [
    firstName,
    lastName,
  ]);

  return <div>{renderFullName}</div>;
}
复制代码

useReducer

その使用法を理解するために簡単な例を見てください。

import React, { useReducer } from "react";
const initState = {
  userName: '匿名',
}

const reducer = (state: any, action: any) => {
  switch (action.type) {
    case 'add':
      return {
        ...state,
        userName: state.userName + '+'
      }
    case 'del':
      return {
        ...state,
        userName: state.userName + '-'
      }
    default:
      return {
        ...state,
        userName: state.userName + '*'
      }
  }
}

export function Login() {
  const [state, dispatch] = useReducer(reducer, initState)

  function handleClick() {
    dispatch({
      type: 'add'
    })
  }
  return (
    <>
      hello, {state.userName}
      <button onClick={handleClick}>按钮</button>
    </>
  )
}
复制代码

終わり

この記事では特に何も説明しませんが、コードの例とデモを示します。

React に触れたことのない初心者にとっては、理解するのが少し難しいかもしれません。

しかし、理解できないことに遭遇したときは、ブラウザの別のウィンドウを開いて検索し、戻って自分が書いた内容を読むこともできます。

最後に、夢の実現に向けて皆さんと一緒に頑張っていきたいと思います。

おすすめ

転載: blog.csdn.net/qq_21473443/article/details/130484320