将检索到的对象推送到 React 中的状态只会给我带来最后一个

Push retrieved objects to state in React only brings me the last one

我正在从 json 中获取数据。每次我尝试将我 return 从 json 传递到我的状态时,我想在我的 React component.But 中显示该数据,它 return 只有一个并删除之前的元素,而不是 json.

中的整个元素

这是我的代码。

const [state, setState] = useState({});

  const connection = new Connection("devnet");
  
  const { publicKey } = useWallet();
  
  useEffect(() => {
    (async () => {
      //if not public key, close
        if(!publicKey) return;
        //get tokens
        let response = await connection.getTokenAccountsByOwner(
          publicKey!, // owner here
          {
            programId: TOKEN_PROGRAM_ID,
          }
        );
        response.value.forEach((e) => {
          const accountInfo = SPLToken.AccountLayout.decode(e.account.data);
          //get only tokens with value of 1
          if ( parseInt(`${SPLToken.u64.fromBuffer(accountInfo.amount)}`) === 1 ) {
            const tokenPublicKey = `${new PublicKey(accountInfo.mint)}`
            //get the metadata of each NFT
            const run = async () => {

              const ownedMetadata = await programs.metadata.Metadata.load(connection, await programs.metadata.Metadata.getPDA(tokenPublicKey));
              //get only tokens of the collection ...
              if (ownedMetadata.data.updateAuthority === "Address_authority") {
              //show the json data from arweave
              let url= ownedMetadata.data.data.uri;
              fetch(url)
              .then(res => res.json())
              .then((out) => {

                setState(prevState => {
                  // THIS IS NOT WORKING FOR ME :(
                  return {...prevState, ...out};
                });
              })
              .catch(err => { throw err });
            } 
          };
  
          run();
          }
        });
        
    })()
    }, [connection, publicKey]);

  console.log(state)

{...prevState, ...out}; 创建一个新对象,将 prevState 自己的所有属性放在新对象上,然后将 out 自己的所有属性放在新对象上 (如果 prevState 也有具有这些名称的属性,则覆盖 prevState 的值。

听起来你想要一个数组,而不是单个对象:

const [state, setState] = useState([]);

然后设置:

setState(prevState => [...prevState, out]);

可能不相关,但这可能是一堆不同的状态变化(response.value 中的每个元素都有一个变化)。因为工作是异步的,所以这也可能导致一些临时重新渲染。也许你想要那个,但如果你不想要,你可以完成所有的获取,然后更新一次状态。此外,任何时候您在 useEffect 中进行异步工作时,您都应该考虑到效果的依赖项在此期间发生变化或组件已卸载的可能性。像这样的东西(见 *** 评论):

const [state, setState] = useState({});

const connection = new Connection("devnet");

const { publicKey } = useWallet();

useEffect(() => {
    // *** Use a controller to stop when the component unmounts, etc.
    const controller = new AbortContoller();
    const {signal} = controller;
    (async () => {
        if (signal.aborted) return; // ***

        // If not public key, stop
        if (!publicKey) return;

        // Get tokens
        let response = await connection.getTokenAccountsByOwner(
            publicKey!, // owner here
            {
                programId: TOKEN_PROGRAM_ID,
            }
        );

        // *** Build up the new data in this array (since we skip some elements,
        // so we won't have a simple 1:1 mapping of `response.value` elements
        // to result elements.
        const newState = [];
        // *** Wait for all the operations to finish and add their elements to `newState`
        await Promise.all(
            response.value.map(async (e) => {
                const accountInfo = SPLToken.AccountLayout.decode(e.account.data);
                // Skip tokens with value other than 1
                if (parseInt(`${SPLToken.u64.fromBuffer(accountInfo.amount)}`) !== 1) {
                    return;
                }
                const tokenPublicKey = `${new PublicKey(accountInfo.mint)}`;
                const ownedMetadata = await programs.metadata.Metadata.load(connection, await programs.metadata.Metadata.getPDA(tokenPublicKey));
                // Get only tokens of the collection ...
                if (ownedMetadata.data.updateAuthority !== "Address_authority") {
                    return;
                }
                // Show the data from arweave
                let url = ownedMetadata.data.data.uri;
                const response = await fetch(url, {signal}); // *** Pass the signal to `fetch`
                if (!response.ok) { // *** This check was missing
                    throw new Error(`HTTP error ${response.status}`); // Or ignore if you prefer
                }
                const out = await response.json();
                newState.push(out);
            })
        );
        // *** Now we have all of them, do one state update
        setState(prevState = [...prevState, ...newState]);
    })();

    return () => {
        // Stop if our dependencies change or the component unmounts
        controller.abort();
    };
}, [connection, publicKey]);