【问题标题】:react memoize using React.memo使用 React.memo 反应记忆
【发布时间】:2022-01-23 15:45:22
【问题描述】:

我尽量不要在 MainAssignment 组件中的 ShowCockpit 状态更改时重新呈现 Persons 组件 就像我在 Cockpit 组件中所做的那样,当 Persons 状态更改时它不会重新呈现。 在这种情况下,我们有 3 个组件 MainAssignment Component [parnt] , Cockpit Component [child] , Persons Component [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;

【问题讨论】:

  • 你能发布Cockpit 组件如何渲染Persons 以及传递给Persons 的道具来自哪里

标签: reactjs


【解决方案1】:

useCallbacktogglePersonHandlerdeletePersonHandler 一起使用

const nameChangeHandler = useCallback((e, id, personIndex) => {
    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);
  }, []);

【讨论】:

    【解决方案2】:

    React.memo可以防止子组件在父组件重新渲染时重新渲染。

    它比较(通过引用)每个前一个和下一个道具。当其中一个不同时,React 将正常重新渲染孩子。

    在您的情况下,您总是将新功能传递给changed prop

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

    如何避免这种情况?

    确保每次需要重新渲染时nameChangeHandler 是相同的函数,并且您不想重新渲染Person 组件。 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
        });
      }, []);
    

    同样你应该记住deletePersonHandler函数

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

    【讨论】:

    • 感谢您的回答。在尝试您的解决方案后,当 showCockpit 状态更改但 nameChangeHandler 无法按预期工作时,请防止人员组件重新呈现。 nameChangeHandler 应该在 Person Component 中的输入值发生变化时运行,并获取该值,然后更新 Person 状态中的特定对象。
    • 当输入值更改 nameChangeHandler 采用此值并更新下一个对象而不是特定对象时会发生什么。例如:当专注于第一人称组件中的输入并单击“k”=> 个人状态 = { id: '1', name: 'mustafa', age: 24 }, { id: '2', name: 'mustafak ', age: 25 }, { id: '3', name: 'mustafa3', age: 26 },然后在第一人称组件中点击“v” => 人物状态 = { id: '1', name :'mustafa',年龄:24 },{ id:'2',名称:'mustafav',年龄:25 },{ id:'3',名称:'mustafa3',年龄:26 },
    • 我认为发生这种情况是因为在使用 useCallback 后,人的状态在 nameChangeHandler 中没有改变,但我不知道该怎么办
    • 我检查了你提供的没有 useCallback 的代码就这样工作codesandbox.io/s/falling-snowflake-ncpv4?file=/src/App.js
    • 真的很感谢你,但是再试一下上面的代码
    猜你喜欢
    • 2020-09-10
    • 2020-08-07
    • 2021-12-04
    • 2019-04-25
    • 1970-01-01
    • 1970-01-01
    • 2021-12-16
    • 1970-01-01
    • 2021-10-21
    相关资源
    最近更新 更多