简体   繁体   中英

How to avoid re-render of React Hooks

Hi I am new developer at ReactJs. I have a question about Hooks. I know there are so many answers about this quest but I did not integrate that solutions to my project. I have parent and child component. I am passing value from child to parent. When I try to send a data via react callback function, parent is re-rendering while taking item and so I lost my selected value on Child. How can I protect that problems? Could you any advice to me?

Parent Component:

import React,{useCallback,useState} from 'react'

const Dropable = ({item}) => {
    const [list, setList] = useState(item)
    const [selectedItems, setSelectedItems] = useState<Array<any>>([])

    const handleSelectedItems = useCallback( (data: any) => {
        if (data.type === "player") {
            if (selectedItems.length === 0) {
                const arr = selectedItems
                arr.push(data)
                setSelectedItems(arr)
            } 
    }, [selectedItems],
    )

    return (
        {list.map((data: any, index: any) => {        
            return (
                <>
                    {
                        <div onClick={() => handleSelectedItems(index, data)}>   
                            <Detailed
                                key={uuid()}
                                data={data}
                                dataIndex={index}               
                            />
                        </div>
                    }
                </>
            )
        })}
    )
}
export default Dropable;

Child Component:

import React,{useState} from 'react'
    const Detailed : React.FC<IProps> = (props) {
    const [selected, setSelected] = useState(false)

    const handleSelect = () => {

        if (selected) {
            setSelected(false)
        }
        else {
            setSelected(true)
        }
    }
     return (
        <div  onClick={handleSelect} className="detail" style={selected?{border: " 1px solid #5c6ef5"}: {}}>       
        </div>
     )
 }

 export default Detailed;

You can simply pass the selected value from parent to child component and initiate the selected state using that props.

const [selected, setSelected] = useState(props.selected)

Then every time the parent updates, the child's selected state will not be null. You can pass null as initial value for selected via props. So, you will call the child component like this,

import React,{useCallback,useState} from 'react'

const Dropable = ({item}) => {
const [list, setList] = useState(item)
const [selectedItems, setSelectedItems] = useState<Array<any>>([])

const handleSelectedItems = useCallback( (data: any) => {
    if (data.type === "player") {
        if (selectedItems.length === 0) {
            const arr = selectedItems
            arr.push(data)
            setSelectedItems(arr)
        } 
}, [selectedItems],
)

return (
    {list.map((data: any, index: any) => { 
        const isSelected = selectedItems.findIndex(item => item.[some_unique_attribute] === data.[some_unique_attribute]) > -1;       
        return (
            <>
                {
                    <div onClick={() => handleSelectedItems(index, data)}>   
                        <Detailed
                            key={uuid()}
                            data={data}
                            dataIndex={index}               
                            selected={isSelected}
                        />
                    </div>
                }
            </>
        )
    })}
  )
}

Hi Instead of maintaining the boolean value in the child to see if the item is selected or not you can pass that value from parent to child. So the parent component:

   const Dropable = ({ item }) => {
   const [list, setList] = useState(item)
   const [selectedItems, setSelectedItems] = useState<Array<any>>([])

   const handleSelectedItems = useCallback((data: any) => {
    if (data.type === "player") {
      if (selectedItems.length === 0) {
        const arr = selectedItems
        arr.push(data)
        setSelectedItems(arr)
      }
    }, [selectedItems],
    )

  return (
    {
      list.map((data: any, index: any) => {
        return (
          <>
            {
              <div onClick={() => handleSelectedItems(index, data)}>
                <Detailed
                  key={uuid()}
                  data={data}
                  dataIndex={index}
                  selected={selectedItems[0][ANY_KEY] === data[ANY_key]}

                />
              </div>
            }
          </>
        )
      })
    }
  )
}
export default Dropable;

And your child component: There is no need to use useState to set the value coz we are doing the same work in the parent component.

   import React,{useState} from 'react'
    const Detailed : React.FC<IProps> = (props) {
     return (
        <div className="detail" style={props.selected?{border: " 1px solid 
     #5c6ef5"}: {}}>       
        </div>
     )
 }

 export default Detailed;

One more tip: It is not a good practice to send data from child component to parent.

The only reason that your child loses its state when the parent re-renders is because you are assigning a new key to the Child component while mapping

Assigning a key that is unique but also remains same across re-renders will prevent this problem from happening

If the key to a component changes, its not re-rendered but re-mounted and hence state values are reset

Either you can use a unique value as key from data or alternative use index if there is no unique key in data. However you must try to have atleast 1 field in data which can be used to uniquely identify it

return (
    {
      list.map((data: any, index: any) => {
        return (
          <>
            {
              <div onClick={() => handleSelectedItems(index, data)}>
                <Detailed
                  key={data.id} // if no id field. You can assign index
                  data={data}
                  dataIndex={index}
                  selected={selectedItems[0][ANY_KEY] === data[ANY_key]}

                />
              </div>
            }
          </>
        )
      })
    }
  )

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