英文:
How can I persist the value of a variable updated when passing it down to a child component in ReactJs?
问题
I'm trying to pass down the variable selectedContactId
from a useState
. I'm console logging the variable in the parent component and it is updating its value and then I'm console logging the prop received in the child component, it is successfully received the updated value, however, then it prints the console log again and the value is set as undefined
.
I guess it is because it is re-rendering when jumping to the new component, so I've tried different ways to persist it, and what seems more reliable to me is using the useMemo()
state, and I'm trying to implement it but it is still not working. If is there an easier way to do that, I'd appreciate you sharing that with me.
Codesandbox replication of error code
Here is my code and a screenshot of the console logs.
ContactTable.jsx
const ContactTable = () => {
const { handleNavigation } = useContext(ContactContext)
const [selectedContactId, setSelectedContactId] = useState(0);
const memoizedSelectedContactId = useMemo(() => selectedContactId, [selectedContactId]);
const isEven = (idx) => idx % 2 === 0;
useEffect(() => {
getContactList(currentPage)
}, [])
return (
<div>
<div className="w-full max-w-full flex flex-col items-center justify-center pt-6 pb-10 pl-10 pr-10">
<Tittle className="text-2xl font-semibold text-orange-500"> Clientes</Tittle>
<Table id="table-to-xls">
<TableHead>
<TableRow>
<TableHeader>ID/NIT</TableHeader>
<TableHeader>OPCIONES</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{contactList?.content?.map((contact, idx) => (
<TableRow key={contact.map.seq_id} className={isEven(idx) ? "" : "bg-sky-50"}>
<TableData>{contact.map.id}</TableData>
<TableData>
<BoxSelectorOptions>
<SelectOptions
onChange={(e) => {
const selectedValue = parseInt(e.target.value);
console.log("Selected Value: " + selectedValue);
setSelectedContactId(selectedValue);
}}
onClick={handleNavigation}
>
<option>[Ir a...]</option>
<option value={contact.map.id} data-url="/followUp">
Seguimiento
</option>
</SelectOptions>
</BoxSelectorOptions>
</TableData>
</TableRow>
))}
</TableBody>
</Table>
{selectedContactId !== 0 && contactList && ( // Add additional rendering
<CollectionPortfolioFollowUps
contactIdentifier={memoizedSelectedContactId}
/>
)}
</div>
</div>
)
}
export default ContactTable
CollectionPortfolio.jsx
import React, { useEffect, useContext, useState } from 'react';
const CollectionPortfolioFollowUps = ({ contactIdentifier }) => {
console.log("It was received in CollectionPor contact ID: " + contactIdentifier)
return (
<div>
<p>Contact ID: {contactIdentifier}</p>
</div>
)
}
const MemoizedCollectionPortfolioFollowUps = React.memo(CollectionPortfolioFollowUps);
const MemoizedWrapperComponent = ({ contactIdentifier }) => {
const memoizedContactIdentifier = useMemo(() => contactIdentifier, [contactIdentifier]);
return <MemoizedCollectionPortfolioFollowUps contactIdentifier={memoizedContactIdentifier} />;
}
export default MemoizedWrapperComponent;
In the child component, I've tried to implement useMemo
as well to avoid re-rendering and updating the prop value as undefined
.
英文:
I'm trying to pass down the variable selectedContactId
from a useState
. I'm console logging the variable in the parent component and it is updating its value and then I'm console logging the prop received in the child component, it is successfully received the updated value, however, then it prints the console log again and the value is set as undefined
.
I guess it is because it is re-rendering when jumping to the new component, so I've tried different ways to persist it, and what seems more reliable to me is using the useMemo()
state, and I'm trying to implement it but it is still not working. If is there an easier way to do that, I'd appreciate you sharing that with me.
Sandox replication of error code
Here is my code and a screenshot of the console logs.
ContactTable.jsx
const ContactTable = () => {
const { handleNavigation } = useContext(ContactContext)
const [selectedContactId, setSelectedContactId] = useState(0);
const memoizedSelectedContactId = useMemo(() => selectedContactId, [selectedContactId]);
const isEven = (idx) => idx % 2 === 0;
useEffect(() => {
getContactList(currentPage)
}, [])
return (
<div>
<div className="w-full max-w-full flex flex-col items-center justify-center pt-6 pb-10 pl-10 pr-10" >
<Tittle className="text-2xl font-semibold text-orange-500"> Clientes</Tittle>
<Table id="table-to-xls">
<TableHead>
<TableRow>
<TableHeader>ID/NIT</TableHeader>
<TableHeader>OPCIONES</TableHeader>
</TableRow>
</TableHead>
<TableBody>
{contactList?.content?.map((contact, idx) => (
<TableRow key={contact.map.seq_id} className={isEven(idx) ? "" : "bg-sky-50"} >
<TableData>{contact.map.id}</TableData>
<TableData>
<BoxSelectorOptions>
<SelectOptions
onChange={(e) => {
const selectedValue = parseInt(e.target.value);
console.log("Selected Value: " + selectedValue);
setSelectedContactId(selectedValue);
}}
onClick={handleNavigation}
>
<option>[Ir a...]</option>
<option value={contact.map.id} data-url="/followUp">
Seguimiento
</option>
</SelectOptions>
</BoxSelectorOptions>
</TableData>
</TableRow>
))}
</TableBody>
</Table>
{selectedContactId !== 0 && contactList && ( // Add aditional rendering
<CollectionPortfolioFollowUps
contactIdentifier={memoizedSelectedContactId}
/>
)}
</div>
</div>
)
}
export default ContactTable
CollectionPortfolio.jsx
import React, { useEffect, useContext, useState } from 'react'
const CollectionPortfolioFollowUps = ({ contactIdentifier }) => {
console.log("It was received in CollectionPor contact ID: " + contactIdentifier) { contactIdentifier, contactList }
return (
<div>
<p>Contact ID: {contactIdentifier}</p>
</div>
)
}
const MemoizedCollectionPortfolioFollowUps = React.memo(CollectionPortfolioFollowUps);
const MemoizedWrapperComponent = ({ contactIdentifier }) => {
const memoizedContactIdentifier = useMemo(() => contactIdentifier, [contactIdentifier]);
return <MemoizedCollectionPortfolioFollowUps contactIdentifier={memoizedContactIdentifier} />;
}
export default MemoizedWrapperComponent;
In the child component, I've tried to implement also useMemo
to avoid the re-render and updating the prop value as undefined
.
答案1
得分: 2
问题
你在两个地方渲染了CollectionPortfolioFollowUps
。在ContactTable
渲染的CollectionPortfolioFollowUps
实例中,你传递了一个 contactIdentifier
属性。这是带有定义值的控制台日志。
const ContactTable = () => {
...
return (
<div>
<div className="....">
<Table id="table-to-xls">
...
</Table>
{selectedContactId !== 0 && (
<CollectionPortfolioFollowUps
contactIdentifier={selectedContactId} // <-- 在这里定义
/>
)}
</div>
</div>
);
};
在 "followup" 路由上渲染的 CollectionPortfolioFollowUps
实例中没有传递属性。这是带有未定义值的控制台日志。
<BrowserRouter>
<Routes>
{/* <Route path="/"><Layout /></Route> */}
<Route path="/" element={<Contact />} />
<Route
path="/followUp"
element={<CollectionPortfolio />} // <-- 这里没有传递属性
/>
{/* </Route> */}
</Routes>
</BrowserRouter>
解决方案
如果我的理解是正确的,你希望将选定的 selectedContactId
状态值传递给在 "/followup" 上渲染的 CollectionPortfolioFollowUps
组件,那么我建议在导航时传递路由状态。
const ContactTable = () => {
const [selectedContactId, setSelectedContactId] = useState(0);
const navigate = useNavigate();
function handleNavigation(e) {
const url = e.target.selectedOptions[0].dataset.url;
if (url) {
navigate(url, { state: { selectedContactId } });
}
}
return (
....
);
};
import { useLocation } from "react-router-dom";
const CollectionPortfolioFollowUps = () => {
const { state } = useLocation();
const { selectedContactId } = state || {};
useEffect(() => {
console.log({ selectedContactId });
}, [selectedContactId]);
return (
<div>
<p>Contact ID: {selectedContactId}</p>
</div>
);
};
英文:
Issue
You are rendering CollectionPortfolioFollowUps
in two places. In the CollectionPortfolioFollowUps
instance that ContactTable
renders you pass it a contactIdentifier
prop. The is the console log with a defined value.
const ContactTable = () => {
...
return (
<div>
<div className="....">
<Table id="table-to-xls">
...
</Table>
{selectedContactId !== 0 && (
<CollectionPortfolioFollowUps
contactIdentifier={selectedContactId} // <-- defined here
/>
)}
</div>
</div>
);
};
In the CollectionPortfolioFollowUps instance rendered on the "/followup" route there is no passed prop. This is the console log with undefined value.
<BrowserRouter>
<Routes>
{/* <Route path="/" element={<Layout />}> */}
<Route path="/" element={<Contact />} />
<Route
path="/followUp"
element={<CollectionPortfolio />} // <-- no prop passed here
/>
{/* </Route> */}
</Routes>
</BrowserRouter>
Solution
If I'm correct in assuming that the desired behavior is that you want to pass the selected selectedContactId
state value along to the CollectionPortfolioFollowUps
component rendered on "/followup"
then I'd suggest passing in route state when navigating.
const ContactTable = () => {
const [selectedContactId, setSelectedContactId] = useState(0);
const navigate = useNavigate();
function handleNavigation(e) {
const url = e.target.selectedOptions[0].dataset.url;
if (url) {
navigate(url, { state: { selectedContactId } });
}
}
return (
....
);
};
import { useLocation } from "react-router-dom";
const CollectionPortfolioFollowUps = () => {
const { state } = useLocation();
const {selectedContactId} = state || {};
useEffect(() => {
console.log({ selectedContactId });
}, [selectedContactId]);
return (
<div>
<p>Contact ID: {selectedContactId}</p>
</div>
);
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论