如何在将变量传递给ReactJs中的子组件时保持更新后的值?

huangapple go评论69阅读模式
英文:

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.

Console logs track

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.

如何在将变量传递给ReactJs中的子组件时保持更新后的值?

ContactTable.jsx

const ContactTable = () =&gt; {
  const { handleNavigation } = useContext(ContactContext)

  const [selectedContactId, setSelectedContactId] = useState(0);

  const memoizedSelectedContactId = useMemo(() =&gt; selectedContactId, [selectedContactId]);

  const isEven = (idx) =&gt; idx % 2 === 0;

  useEffect(() =&gt; {
    getContactList(currentPage)
  }, [])

  return (
    &lt;div&gt;
      &lt;div className=&quot;w-full max-w-full flex flex-col items-center justify-center pt-6 pb-10 pl-10 pr-10&quot; &gt;
        &lt;Tittle className=&quot;text-2xl font-semibold text-orange-500&quot;&gt; Clientes&lt;/Tittle&gt;
        &lt;Table id=&quot;table-to-xls&quot;&gt;
          &lt;TableHead&gt;
            &lt;TableRow&gt;
              &lt;TableHeader&gt;ID/NIT&lt;/TableHeader&gt;
              &lt;TableHeader&gt;OPCIONES&lt;/TableHeader&gt;
            &lt;/TableRow&gt;
          &lt;/TableHead&gt;
          &lt;TableBody&gt;
            {contactList?.content?.map((contact, idx) =&gt; (
              &lt;TableRow key={contact.map.seq_id} className={isEven(idx) ? &quot;&quot; : &quot;bg-sky-50&quot;} &gt;
                &lt;TableData&gt;{contact.map.id}&lt;/TableData&gt;
                &lt;TableData&gt;
                  &lt;BoxSelectorOptions&gt;
                    &lt;SelectOptions
                      onChange={(e) =&gt; {
                        const selectedValue = parseInt(e.target.value);
                        console.log(&quot;Selected Value: &quot; + selectedValue);
                        setSelectedContactId(selectedValue); 
	                  }}
                      onClick={handleNavigation}
                    &gt;
                      &lt;option&gt;[Ir a...]&lt;/option&gt;
                      &lt;option value={contact.map.id} data-url=&quot;/followUp&quot;&gt;
                        Seguimiento
                      &lt;/option&gt;
                    &lt;/SelectOptions&gt;
                  &lt;/BoxSelectorOptions&gt;
                &lt;/TableData&gt;
              &lt;/TableRow&gt;
            ))}
          &lt;/TableBody&gt;
        &lt;/Table&gt;
        {selectedContactId !== 0 &amp;&amp; contactList &amp;&amp; ( // Add aditional rendering
          &lt;CollectionPortfolioFollowUps 
            contactIdentifier={memoizedSelectedContactId} 
          /&gt;
   	    )}
      &lt;/div&gt;
    &lt;/div&gt;
  )
}

export default ContactTable

CollectionPortfolio.jsx

import React, { useEffect, useContext, useState } from &#39;react&#39;

const CollectionPortfolioFollowUps = ({ contactIdentifier }) =&gt; {
  console.log(&quot;It was received in CollectionPor contact ID: &quot; + contactIdentifier)  { contactIdentifier, contactList }

  return (
    &lt;div&gt;
      &lt;p&gt;Contact ID: {contactIdentifier}&lt;/p&gt;
    &lt;/div&gt;
  )
}

const MemoizedCollectionPortfolioFollowUps = React.memo(CollectionPortfolioFollowUps);

const MemoizedWrapperComponent = ({ contactIdentifier }) =&gt; {
  const memoizedContactIdentifier = useMemo(() =&gt; contactIdentifier, [contactIdentifier]);

  return &lt;MemoizedCollectionPortfolioFollowUps contactIdentifier={memoizedContactIdentifier} /&gt;;
}

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>
  );
};

如何在将变量传递给ReactJs中的子组件时保持更新后的值?

英文:

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 = () =&gt; {
  ...

  return (
    &lt;div&gt;
      &lt;div className=&quot;....&quot;&gt;
        &lt;Table id=&quot;table-to-xls&quot;&gt;
          ...
        &lt;/Table&gt;
        {selectedContactId !== 0 &amp;&amp; (
          &lt;CollectionPortfolioFollowUps
            contactIdentifier={selectedContactId} // &lt;-- defined here
          /&gt;
        )}
      &lt;/div&gt;
    &lt;/div&gt;
  );
};

In the CollectionPortfolioFollowUps instance rendered on the "/followup" route there is no passed prop. This is the console log with undefined value.

&lt;BrowserRouter&gt;
  &lt;Routes&gt;
    {/* &lt;Route path=&quot;/&quot; element={&lt;Layout /&gt;}&gt; */}
    &lt;Route path=&quot;/&quot; element={&lt;Contact /&gt;} /&gt;
    &lt;Route
      path=&quot;/followUp&quot;
      element={&lt;CollectionPortfolio /&gt;} // &lt;-- no prop passed here
    /&gt;
    {/* &lt;/Route&gt; */}
  &lt;/Routes&gt;
&lt;/BrowserRouter&gt;

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 &quot;/followup&quot; then I'd suggest passing in route state when navigating.

const ContactTable = () =&gt; {
  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 &quot;react-router-dom&quot;;

const CollectionPortfolioFollowUps = () =&gt; {
  const { state } = useLocation();
  const {selectedContactId} = state || {};

  useEffect(() =&gt; {
    console.log({ selectedContactId });
  }, [selectedContactId]);

  return (
    &lt;div&gt;
      &lt;p&gt;Contact ID: {selectedContactId}&lt;/p&gt;
    &lt;/div&gt;
  );
};

如何在将变量传递给ReactJs中的子组件时保持更新后的值?

huangapple
  • 本文由 发表于 2023年3月4日 05:22:32
  • 转载请务必保留本文链接:https://go.coder-hub.com/75631992.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定