0

I have a parent component with two children: Child1 that displays the information and Child2 that changes the information through input. My goal is to keep the Child1 information from re-rendering until a save trigger would be passed from the Child2 to parent meaning the data was saved. For that I've used memo in Child1 to memoize the data and keep it from re-rendering but it won't work as the child is still re-rendered every time the onChange event is triggered. How can I make Child1 to keep the data unchanged until it is saved? This is what I have so far:

Child1:

import React,{memo} from 'react'

const UserDetails = ({values,onOpen})=>{

console.log("child1 rendering")
    return(
          <Center>
            <Box d="flex" justifyContent='space-between' w="xs">
              <Heading name="username">{values.username}</Heading>
              <IconButton size="md" icon={<FiPenTool />} onClick={onOpen} />
            </Box>
          </Center>
          <Stack mt={10} direction={['column', 'column']}>
            <Heading fontSize="20px">Location:{values.location}</Heading>

            <Box mt={5} px={20}>
              <Heading fontSize="20px">Description</Heading>
              <Text fontSize="md">{values.description}</Text>
            </Box>
          </Stack>
    )
}

export default memo(UserDetails)

Child2:

      function PopUpDetails({ values, handleDetails })

 {
    console.log("child2 render")
  return (
        <Box>
        <Input name="username" w="md" mt={5} mr={5} fontWeight="bold" textAlign="center"
          value={values.username} onChange={handleDetails} />
        <Input name="location" mt={5} fontWeight="bold" onChange={handleDetails} 
          textAlign="center" 
          w="md" mr={5}
           onFocus={() => onOpen()} value={values.location} />
         
        <Input name="description" w="md" h="20vh" mt={5} mr={5} textAlign="center"
        fontWeight="bold" value={values.description} onChange={handleDetails} />
      
    </Box>
  )

}

export default PopUpDetails

Parent:

const userValues= {
  username: '',
  location:'',
  description:''
}

function User() {
  const { Moralis, user, setUserData, isUserUpdating } = useMoralis()
  const currentUserId = user.id
  const { isOpen, onOpen, onClose } = useDisclosure()
  const params = { "userId": currentUserId }
  const [details, setDetails] = useState(()=>()=>userHook())

 const userHook = useCallback(()=>{ //callback function to retrieve initial values each  time 
                                        user closes child2 in case data was not saved
   setDetails({...details,
    username:Moralis.User.current().attributes.username,
    description: Moralis.User.current().attributes.description,
    location: Moralis.User.current().attributes.location,
  })
  },[])

  useEffect(()=>{
    userHook()
  },[])

  const saveDetails = () =>{
    setUserData({
      location: details.location,
      username: details.username,
      description: details.description,
    })
  }

 const handleInputChange = (e) => {
    const { name, value } = e.target
    setDetails({
      ...details,
      [name]: value
    })
  }

return(
  <Container>
   <UserDetails values={details}/>
    <Modal isOpen={isOpen} size="3xl" closeOnOverlayClick isCentered onClose={() => { onClose();userHook()}}>
        <ModalOverlay />
        <ModalContent >
          <ModalHeader>Edit details</ModalHeader>
          <ModalCloseButton />
          <ModalBody>
            <PopUpDetails values={details} handleDetails={(e) => handleInputChange(e)}/>
          </ModalBody>
          <ModalFooter>
            <Button onClick={() => saveDetails()} variant='ghost'>Save</Button>
          </ModalFooter>
        </ModalContent>
      </Modal>
   </Container>
)

export default User

Can someone help me undestand why Child1 re-renders every time even if I use memo and how can I cache the data so when handleChange is triggered it will not reflect on Child1?Any help would be appreciated!

Icarus23
  • 167
  • 2
  • 11

1 Answers1

1
<UserDetails values={details}/>

your user details is receiving the state in "values" prop, is the state that you are changing in the modal, so why would you expect to not rerendering?

try get your initial values aside, then set it into default state and pass that througth your component to remain the same.

function User() {
  const { Moralis, user, setUserData, isUserUpdating } = useMoralis()
  const { isOpen, onOpen, onClose } = useDisclosure()
  const params = { "userId": user.id }
  const initValues = useMemo(() => {
    const {
      username,
      description,
      location
    } = Moralis.User.current().attributes;
    return { username, description, location };
  }, []);
  const [details, setDetails] = useState(initValues)

  ...

    <UserDetails value={initValues}  />

  ...
guiwme5
  • 712
  • 6
  • 10
  • Thank you for your answer. I've tried as you suggested and it works, the values are not changing, but Child1 it's still rerendering onChange or after I close Child2 along with the parent. It shouldn't remain unrendered considering that the values are memoized? – Icarus23 Feb 11 '22 at 22:36
  • you should show what useMoralis is doing ... but yes, you dont need useCalback there you need useMemo ( the same but for values instead of fn ) and set it to initValues like: `const initValues = useMemo(( ) => return { ` `username: Moralis.User.current().attributes.username,` `description: Moralis.User.current().attributes.description,` `location: Moralis.User.current().attributes.location` `}, []); ` remove the userHook and this is unneeded too: `useEffect(()=>{ userHook() },[])` – guiwme5 Feb 11 '22 at 22:51
  • Thank you for your input, it works! Please update your answer so that I can accept it. – Icarus23 Feb 11 '22 at 23:12