简体   繁体   中英

React - Memo not working when a function is exported to child component by props

I have a problem in React. The problem in question is about the use of the memo, basically, I have a list generator application, and I use the memo in the list item(child) component, so far so good, working. The problem is that when I export functions through props, that is, functions from the parent component to the child component, the memo doesn't work.

The goal is for the memo, to do its default purpose with the list items, and not render everything in the virtual dom.

  import React from 'react'
  import {memo} from 'react'
  import "./Task.css"
  import  junk from "../../img/junk.png"
  import  star from "../../img/star.png"
  import  starFull from "../../img/star-full.png"
  import  verifyFull from "../../img/verify-full.png"
  import  verify from "../../img/verify.png"

  const Task = (props) => {
    return (
      <div className='task'>
          <div className='task_title'>
            <h2>{props.title}</h2>
            <div className='task_settings' >
              <button onClick={props.handleFavorite}><img src= {props.favorite?starFull:star} alt="" /></button>
              <button onClick={props.handleComplete}><img src= {props.complete?verifyFull:verify} alt="" /></button>
              <button onClick={props.handleDelete} ><img src={junk} alt="" /></button>            
            </div>
          </div>
          <div className='task_description'>
            {props.description}
          </div>
          <div>
            {props.date}
          </div>
      </div>
    )

/------------------------------------------

import './TaskManeger.css'
import {Task} from "../../components";
import React, {useState,useEffect,memo} from 'react'

const TaskManeger = () => {

  const [formValues,setValues] = useState({
    title:"",
    description:"",
    date:""}
  )

  const [list,setList] = useState([]
  )

  function maskDate(value) {
    return value
      .replace(/\D/g, "")
      .replace(/(\d{2})(\d)/, "$1/$2")
      .replace(/(\d{2})(\d)/, "$1/$2")
      .replace(/(\d{4})(\d)/, "$1");
  };

  function handleInput(e) {
    const {name,value} = e.target
    setValues(name==="date"?{...formValues,[name]:maskDate(value)}:{...formValues,[name]:value})    
  }//set values in input

  function handleSubmit(e) {
    e.preventDefault()
    const formData = new FormData(e.target)
    const data = Object.fromEntries(formData)
    addTask(data)
  }//creat object with values for input

  function handleAddFavorite(id) {
    setList(list.map( itemList => {return itemList.id === id ?  { ...itemList, favorite: !itemList.favorite } : itemList}))
  }

  function handleAddComplete(id) {
    setList(list.map( itemList => {return itemList.id === id ?  { ...itemList, complete: !itemList.complete } : itemList}))
  }

  function deleteTask(item) {
    setList(list.filter(i=> i.id !== item.id))
  }

  function addTask(e) {
    setList([...list,{title:e.title, description:e.description, date:e.date, id:list.length,favorite:false,complete:false}])
  }//creat object in list 
  
  useEffect(() => {
    setList([...list,
    {title:"Siga As Instrtuções ", description:"Abaixo nos temos varias tarefas, e nos iremos passar por um tutorial em cada uma delas, siga e conforme for completando-as, conclua no icone de visto a acima.", date:"", id:0,favorite:false},
    {title:"Crie Suas Tarefas", description:"Ao lado, podemos ver um formulario, onde você pode preencher as informações para você mesmo criar suas tarefas.", date:"", id:1,favorite:false},
    {title:"Favorite Suas Tarefas", description:"Você pode favoritar as tarefas atravez da estrela que esta acima, a direita do seu titulo.", date:"", id:2,favorite:false},
    {title:"Apague Suas Tarefas", description:"Você pode apagar as tarefas atravez da lixeira que esta acima, a direita do seu titulo.", date:"", id:3,favorite:false},
    {title:"Filtre", description:"Alem disso tudo que ja falamos nas tarefas anteriores, você pode filtrar suas tarefas, caso queira ver apenas as completadas, ou as que ainda faltam ser completadas, as favoritas, ou todas.", date:"", id:4,favorite:false}])
  }, [])
  
  return (
    <div className="tasks">
      <div className="tasks_container">
        <div className="task_generator">
          <form onSubmit={handleSubmit}>
            <input placeholder='Titulo' name='title' type="text" onChange={handleInput} value={formValues.title}/>
            <input placeholder='Descrição'  name='description' type="text" onChange={handleInput} value={formValues.description}    />
            <input placeholder='Data' name='date' onChange={handleInput} value={formValues.date} />
            <button type="submit">Criar</button>
          </form>  
        </div>
        <div className="task_list">
            <div className='task_filter'>
              <div>Filtrar ▼</div>
              <p>Todos</p>
              <p>Completados</p>
              <p>Favoritos</p>
            </div>
            {list.map( itemList =>(
                <Task 
                  key={itemList.id}
                  title={itemList.title}
                  description={itemList.description}
                  date={itemList.date}
                  handleDelete={()=>deleteTask(itemList)}
                  handleFavorite={()=>handleAddFavorite(itemList.id)}
                  handleComplete={()=>handleAddComplete(itemList.id)}
                  id={itemList.id}
                  favorite={itemList.favorite}
                  complete={itemList.complete}
              />
            ))}          
        </div>        
      </div>
    </div>
  ) 
}

export  default TaskManeger

The memo is not working because the functions you are passing in are not referentially stable

handleDelete={()=>deleteTask(itemList)}
handleFavorite={()=>handleAddFavorite(itemList.id)}
handleComplete={()=>handleAddComplete(itemList.id)}

You create three new functions on every render and pass them as prop. memo tests for referential equality ( === ) of each prop, but (() => x) !== (() => x) .

You could pass in the functions like so:

handleDelete={deleteTask}
handleFavorite={handleAddFavorite}
handleComplete={handleAddComplete}

and make the child component pass the argument instead.

You would also need to use useCallback for those functions to get stability.

const deleteTask = useCallback((item) => {
    setList(list => list.filter(i => i.id !== item.id))
}, [setList])

// repeat pattern for all three functions

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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