banner
SlhwSR

SlhwSR

热爱技术的一名全栈开发者
github
bilibili

render渲染時機

一、原理#

まず、render関数はreact内で 2 つの形式があります:

クラスコンポーネントでは、renderメソッドを指します:

class Foo extends React.Component {
    render() {
        return <h1> Foo </h1>;
    }
}

関数コンポーネントでは、関数コンポーネント自体を指します:

function Foo() {
    return <h1> Foo </h1>;
}

render内では、jsxを記述します。jsxbabelによってコンパイルされ、私たちが知っているjs形式に変換されます。以下のようになります:

return (
  <div className='cn'>
    <Header> hello </Header>
    <div> start </div>
    Right Reserve
  </div>
)

babelによるコンパイル後:

return (
  React.createElement(
    'div',
    {
      className : 'cn'
    },
    React.createElement(
      Header,
      null,
      'hello'
    ),
    React.createElement(
      'div',
      null,
      'start'
    ),
    'Right Reserve'
  )
)

名前からわかるように、createElementメソッドは要素を作成するために使用されます。

reactでは、この要素は仮想 DOM ツリーのノードであり、3 つのパラメータを受け取ります:

  • type:タグ

  • attributes:タグの属性、存在しない場合は null

  • children:タグの子ノード

これらの仮想 DOM ツリーは最終的に実際の DOM にレンダリングされます。

renderプロセスでは、Reactは新しく呼び出されたrender関数から返されたツリーと古いバージョンのツリーを比較し、これはDOMをどのように更新するかを決定するための必要なステップです。その後、diff比較が行われ、DOMツリーが更新されます。

二、トリガータイミング#

renderの実行タイミングは主に 2 つに分けられます:

  • クラスコンポーネントでの setState による状態の変更
class Foo extends React.Component {
  state = { count: 0 };

  increment = () => {
    const { count } = this.state;

    const newCount = count < 10 ? count + 1 : count;

    this.setState({ count: newCount });
  };

  render() {
    const { count } = this.state;
    console.log("Foo render");

    return (
      <div>
        <h1> {count} </h1>
        <button onClick={this.increment}>Increment</button>
      </div>
    );
  }
}

ボタンをクリックすると、setStateメソッドが呼び出され、countが変化していない場合でもコンソールにFoo renderが出力され、renderが実行されることが証明されます。

  • useState フックを使用した関数コンポーネントでの状態の変更
function Foo() {
  const [count, setCount] = useState(0);

  function increment() {
    const newCount = count < 10 ? count + 1 : count;
    setCount(newCount);
  }

  console.log("Foo render");
  
  return (
    <div>
      <h1> {count} </h1>
      <button onClick={increment}>Increment</button>
    </div>
  );
}

関数コンポーネントでは、useStateを使用してデータを更新する形式で、配列の値が変更されない限り、renderはトリガされません。

  • クラスコンポーネントの再レンダリング
class App extends React.Component {
  state = { name: "App" };
  render() {
    return (
      <div className="App">
        <Foo />
        <button onClick={() => this.setState({ name: "App" })}>
          Change name
        </button>
      </div>
    );
  }
}

function Foo() {
  console.log("Foo render");

  return (
    <div>
      <h1> Foo </h1>
    </div>
  );
}

Appコンポーネント内のChange nameボタンをクリックすると、Fooの具体的な実装に関係なく、renderが再度実行されます。

  • 関数コンポーネントの再レンダリング
function App(){
    const [name,setName] = useState('App')

    return (
        <div className="App">
            <Foo />
            <button onClick={() => setName("aaa")}>
                { name }
            </button>
      </div>
    )
}

function Foo() {
  console.log("Foo render");

  return (
    <div>
      <h1> Foo </h1>
    </div>
  );
}

useStateを使用して状態を更新する場合、最初のレンダリング時にのみFoo renderがトリガされ、後続の更新ではFoo renderが発生しないことがわかります。

三、まとめ#

render関数ではJSXを記述し、createElementのような形式に変換して仮想 DOM を生成し、最終的に実際の DOM に変換します。

Reactでは、クラスコンポーネントでsetStateメソッドが実行されると必ずrender関数が実行されますが、関数コンポーネントではuseStateを使用して状態を変更しても必ずしもrenderがトリガされるわけではありません。

コンポーネントのpropsが変更された場合、必ずしもrender関数が実行されるわけではありませんが、propsの値が親コンポーネントまたは祖先コンポーネントのstateから取得される場合、

この場合、親コンポーネントまたは祖先コンポーネントのstateが変更されると、子コンポーネントが再レンダリングされます。

したがって、setStateが実行されるとrenderメソッドが実行され、useStateは現在の値が変更されたかどうかを判断してrenderメソッドを実行します。親コンポーネントがレンダリングされると、子コンポーネントもレンダリングされます。

image

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。