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!
<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} />
...