简体   繁体   English

使用 React.memo 反应记忆

[英]react memoize using React.memo

I Try not to Rerender Persons Component When ShowCockpit State Changes In MainAssignment Component.当 MainAssignment 组件中的 ShowCockpit State 更改时,我尽量不重新渲染Persons组件。 Like when i do in Cockpit Component, it doesn't rerender When Persons state change.就像我在 Cockpit Component 中所做的那样,当 Persons state 更改时,它不会重新呈现。 In This Case We Have 3 Components MainAssignment Component [parnt], Cockpit Component [child], Persons Component [child].在这种情况下,我们有 3 个组件 MainAssignment 组件 [parnt]、Cockpit 组件 [child]、Persons 组件 [child]。

/********************************************************/
/*** MainAssignment Component ***/

import React, { useCallback, useState } from 'react';
import Persons from './persons';
import Coockpit from './cockpit';

const MainAssignment = () => {
  // All State
  const [persons, setPersons] = useState([
    { id: '1', name: 'mustafa', age: 24 },
    { id: '2', name: 'ahmed', age: 25 },
    { id: '3', name: 'saad', age: 26 },
  ]);
  const [showPersons, setShowPersons] = useState(true);
  const [showCoockpit, setShowCoockpit] = useState(true);

  const togglePersonHandler = useCallback(() => {
    setShowPersons(!showPersons);
  }, [showPersons]);

  // change name in specific object in persons state
  const nameChangeHandler = (e, id, personIndex) => {
    let newPersons = [...persons];
    let person = { ...newPersons[personIndex] };
    person.name = e.target.value;

    newPersons[personIndex] = person;

    setPersons(newPersons);
  };

  // delete object from persons state
  const deletePersonHandler = (personIndex) => {
    let newPersons = [...persons];
    newPersons.splice(personIndex, 1);
    setPersons(newPersons);
  };

  // Main Render
  return (
    <>
      <button
        onClick={() => {
          setShowCoockpit((prev) => !prev);
        }}
      >
        remove Coockpit
      </button>
      {showCoockpit ? (
        <div style={{ border: '1px solid' }}>
          <Coockpit clicked={togglePersonHandler} personsLength={persons.length} showPersons={showPersons} />
        </div>
      ) : null}
      {showPersons ? <Persons persons={persons} clicked={deletePersonHandler} changed={nameChangeHandler} /> : null}
    </>
  );
};

export default MainAssignment;


/********************************************************/
/*** Cockpit Component ***/

/********************************************************/
/*** Cockpit Component ***/

import React, { useRef } from 'react';

const Cockpit = ({ clicked }) => {
  let toggleBtnRef = useRef(null);

  console.log('render => Cockpit');

  return (
    <div>
      <h1>hi i'm a main assin from cockpit</h1>
      <button className="toggle-persons" onClick={clicked} ref={toggleBtnRef}>
        toggle persons
      </button>
    </div>
  );
};

// in Cockpit i use React.memo and it work
export default React.memo(Cockpit);


/********************************************************/
/*** Persons Component ***/

import React, { useEffect, useRef } from 'react';
import Person from './person';

const Persons = ({ persons, clicked, changed }) => {
  console.log('render => personssss');

  const mainRef = {
    allInputPersonRef: useRef([]),
  };

  return (
    <>
      {persons?.map((person, idx) => (
        <Person
          key={idx}
          name={person.name}
          age={person.age}
          position={idx}
          index={idx}
          ref={mainRef}
          click={() => {
            clicked(idx);
          }}
          changed={(e) => {
            changed(e, person.id, idx);
          }}
        />
      ))}
    </>
  );
};

// in Persons i use React.memo and it doesn't work
export default React.memo(Persons);


/********************************************************/
/*** Person Component ***/

import React from 'react';

const Person = React.forwardRef((props, ref) => {
  const { allInputPersonRef } = ref;

  // value of props
  const { name, age, click, changed, children, index } = props;

  return (
    <div>
      <p onClick={click}>
        i'm {name} and i'm {age} years old
      </p>
      <p> i'am props children: {children}</p>
      <input type="text" onChange={changed} value={name} ref={(el) => (allInputPersonRef.current[index] = el)} />
      <button onClick={click}>delete this person</button>
    </div>
  );
});

export default Person;

React.memo can prevent children from rerendering when the parent component rerenders. React.memo可以防止子组件在父组件重新渲染时重新渲染。

It compares (by reference) each previous and next prop.它比较(通过引用)每个前一个和下一个道具。 When one of them is different React will rerender the child normally.当其中一个不同时,React 将正常重新渲染孩子。

In your case you are always passing new function to changed prop在您的情况下,您总是将新的 function 传递给changed的道具

 const nameChangeHandler = (e, personIndex) => {
    let newPersons = [...persons];
    let person = { ...newPersons[personIndex] };
    person.name = e.target.value;

    newPersons[personIndex] = person;
    setPersons(newPersons);
  };

How to avoid this?如何避免这种情况?

Make sure that nameChangeHandler is the same function each time you need to rerender and you don't want to rerender the Person component.确保每次需要重新渲染并且不想重新渲染Person组件时nameChangeHandler与 function 相同。 https://reactjs.org/docs/hooks-reference.html#usecallback https://reactjs.org/docs/hooks-reference.html#usecallback

 const nameChangeHandler = useCallback((e, personIndex) => {
    setPersons((persons) => {
        let newPersons = [...persons];
        let person = { ...newPersons[personIndex] };
        person.name = e.target.value;

        newPersons[personIndex] = person;
        return newPersons
    });
  }, []);

Similarly you should memorize deletePersonHandler function同样,您应该记住deletePersonHandler andler function

const deletePersonHandler = useCallback((personIndex) => {
    setPersons((persons)=>{
        let newPersons = [...persons];
        newPersons.splice(personIndex, 1);
        return newPersons
    });
}, []);

using useCallback on togglePersonHandler and deletePersonHandler在 togglePersonHandler 和 deletePersonHandler 上使用 useCallback

    let newPersons = [...persons];
    let person = { ...newPersons[personIndex] };
    person.name = e.target.value;
    newPersons[personIndex] = person;
    setPersons(newPersons);
  }, []);
const deletePersonHandler = useCallback((personIndex) => {
    let newPersons = [...persons];
    newPersons.splice(personIndex, 1);
    setPersons(newPersons);
  }, []);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM