簡體   English   中英

state 更新后反應不重新渲染

[英]React not re-rendering after state update

我目前正在學習 React。 在我的主頁組件中,我使用掛鈎來初始化和填充 state。 我有 2 個狀態,1 個包含一個隨機口袋妖怪,另一個是 20 個隨機口袋妖怪的數組。 第一個工作正常,但不是數組。

這是主頁組件:

// Hook
import { useHomeFetch } from "../hooks/useHomeFetch";
// Image
import NoImage from '../images/missingno.png';

const Home = () => {
    const { state, collection, loading, error } = useHomeFetch();
    return (
        <>
            { state.heroPokemon ?
            <HeroImage 
                image={`${API.fetchSprite(state.heroPokemon)}`}
                title={state.heroPokemon.name}
                text='Placeholder, should include abilities, etc. to show the pokemon off.'
            />
            : null}
            { collection.pokemons[0] ?
            <Grid header='Popular pokemons'>
                {collection.pokemons.map( pokemon => (
                    <h3 key={pokemon.id}>{pokemon.name}</h3>
                ) )}
            </Grid>
            : null}
        </>
    );
}

export default Home;

添加條件渲染后, heroPokemon 工作正常。 我的第一個想法是對集合做同樣的事情,因為它可能在所有 API 調用承諾解決之前就已經呈現了。

如果我查看反應開發工具,這就是我看到的 state:

hooks
HomeFetch

1 State : {heroPokemon: {…}}
2 State : {pokemons: Array(20)}
           pokemons :
               [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, …]

所以在我看來,調用確實完成得很好,里面有 20 個隨機的口袋妖怪對象。

這是鈎子:

const initialState = {
    heroPokemon: null
}

const fetchRandomPokemons = async (ids) => {
    let pokemons = [];
    ids.forEach(async num => {
        pokemons.push(await API.fetchPokemon(num));
    });
    return pokemons;
}

export const useHomeFetch = () => {
    const [state, setState] = useState(initialState);
    const [collection, setCollection] = useState({pokemons: []});
    const [loading, setLoading] = useState(false);
    const [error, setError] = useState(false);
    
    const fetchPokemons = async (limit = 20) => {
        try {
            setError(false);
            setLoading(true);
            
            const heroPokemon = await API.fetchPokemon(randomInt(1, 898));
            const randomInts = randomUniqueInt(1, 898, limit);
            const pokemons = await fetchRandomPokemons(randomInts);

            setState(prev => ({heroPokemon}));
            setCollection(prev => ({pokemons}));
        } catch (error) {
            setError(true);
        }
        setLoading(false);
        
    };
    // Initial render
    useEffect(() => {
        fetchPokemons(20);
    }, [])

    return { state, collection, loading, error };
};

當我取出條件時, Grid出現了,但它沒有帶有口袋妖怪名稱的 h3。 如果我 console.log 退出state並在home之前collection 最后幾個顯示正確的state.heroPokemoncollection.pokemons填充正確。 這讓我相信狀態已正確更新,但為什么 React 不重新渲染網格組件?

要添加,我還嘗試添加:

<p>{collection.pokemons[0]}</p>

到 home 組件,什么也沒有出現。 我覺得我可能誤解或錯過了狀態和重新渲染如何工作的關鍵部分,這導致我錯過了我可能做錯的事情。

嘗試這個:

 // Hook
import { useHomeFetch } from "../hooks/useHomeFetch";
// Image
import NoImage from '../images/missingno.png';

const Home = () => {
    const [newCollection, setNewCollection] =useState([])
    const { state, collection, loading, error } = useHomeFetch();
    
    useEffect(()=>{
       setNewCollection(collection || []);
     },[collection])

 
    return (
        <>
            { state.heroPokemon ?
            <HeroImage 
                image={`${API.fetchSprite(state.heroPokemon)}`}
                title={state.heroPokemon.name}
                text='Placeholder, should include abilities, etc. to show the pokemon off.'
            />
            : null}
            { newCollection.pokemons[0] ?
            <Grid header='Popular pokemons'>
                {newCollection.pokemons.map( pokemon => (
                    <h3 key={pokemon.id}>{pokemon.name}</h3>
                ) )}
            </Grid>
            : null}
        </>
    );
}

export default Home;

如果仍然不起作用,則分享您的回購。 所以我可以幫助你更多。 謝謝

問題

似乎問題出在您的fetchRandomPokemons實用程序 function 中。 即使您已經聲明fetchRandomPokemons async ,您也不會等待任何東西。 Array.prototype.forEach也是完全同步的。 但是,您確實.forEach回調聲明為asyncawait API 響應,但由於.forEach是同步的,因此fetchRandomPokemons function 已經返回了空的pokemons數組並已解決。 async關鍵字只允許 function scope 使用await關鍵字。

const fetchRandomPokemons = async (ids) => {
  let pokemons = [];
  ids.forEach(async num => { // <-- synchronous
    pokemons.push(await API.fetchPokemon(num));
  });
  return pokemons; // <-- returns []
}

...

const pokemons = await fetchRandomPokemons(randomInts); // <-- []

setCollection({ pokemons }); // <-- { pokemons: [] }

您正在記錄的是當每個API.fetchPokemon Promise 解析時pokemons數組發生突變的.forEach數組。

解決方案

Map 將ids分配到 Promise 數組中,然后等待它們全部解析並返回已解析的口袋妖怪數組。

const fetchRandomPokemons = (ids) => {
  return Promise.all(ids.map(API.fetchPokemon));
}

...

const fetchPokemons = async (limit = 20) => {
  setError(false);
  setLoading(true);

  try {
    const heroPokemon = await API.fetchPokemon(randomInt(1, 898));
    const randomInts = randomUniqueInt(1, 898, limit);
    const pokemons = await fetchRandomPokemons(randomInts);

    setState({ heroPokemon });
    setCollection({ pokemons });
  } catch (error) {
    setError(true);
  }
  setLoading(false);
};

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM