J'ai un composant parent et un composant enfant.

Le composant parent a une variable d'état à laquelle je souhaite accéder.

Je passe une fonction au composant enfant et l'appelle là onClick.

Cependant, la valeur de la variable d'état est obsolète - pas la valeur actuelle.

function Parent(props) {
    const [items, setItems] = useState([]);

    function getItems () {
        console.log(items); // always prints "[]" 
    }

    useEffect(() => {
        thirdPartyApiCall().then((fetchedItems) =>
            let newItems = fetchedItems.map(item => <Child id={item.id} getItems={getItems}/>)
            setItems(newItems); // populates items array. "{items}" elements correctly displayed on web page
        );
    }, []);

}


function Child(props) {
    return <button onClick={props.getItems}>{props.id}</button>
}
2
Samat Davletshin 10 mars 2019 à 20:09

2 réponses

Meilleure réponse

Eh bien, oui, bien sûr en cliquant sur le bouton logs []. Dans ce:

<Child id={item.id} getItems={getItems}/>

getItem est évalué en tant que fonction qui enregistre les éléments et les éléments sont [], donc [] est enregistré. La raison pour laquelle il n'enregistre pas les nouveaux éléments est que, pendant l'écriture de votre code, React ne peut pas vraiment savoir quand mettre à jour le composant, car aucune variable n'est utilisée dans la phase de rendu.


C'est généralement une mauvaise pratique d'utiliser des éléments JSX en dehors de Render car ce ne sont pas vraiment des variables JS normales avec un comportement prévisible. Le fait qu'il compile est plus un hack qu'une fonctionnalité.

Conservez les variables vanilla dans l'état et laissez les composants effectuer le rendu JSX. Cette approche est légèrement différente de la vôtre, mais fonctionne comme prévu, car un clic sur l'un des boutons enregistre tous les éléments :

const fakeApiCall = () => Promise.resolve([
    {id: 10},
    {id: 20},
    {id: 30},
]);

const Parent = () => {
    const [items, setItems] = React.useState([]);
    React.useEffect(() => {
        fakeApiCall().then(setItems)
    })
    const getItems = () => console.log(items);
    return (
        <div>
            {items.map(item => (
                <Child
                id={item.id}
                key={item.id}
                getItems={getItems}/>
            ))}
        </div>
    )
}

const Child = ({id, getItems}) => (<button onClick={getItems}>{id}</button>);

window.onload = () => ReactDOM.render(<Parent/>, document.querySelector('#root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>

<div id="root"></div>
2
Nino Filiu 10 mars 2019 à 18:07

Vous voudrez peut-être examiner le fonctionnement de la closure.

Donnera une idée de base de ce qui se passe.

useEffect ne s'exécuterait qu'une seule fois, en raison de la dépendance. Ainsi, lorsque les composants enfants ont été rendus, items était un tableau vide ([]). Vos composants enfants auront toujours cette instance de items car useEffect n'a pas été appelé avec items mis à jour.

J'espère que cela vous a aidé.

MODIFIER : 1

Vous voudrez peut-être que votre composant parent ressemble à ceci.

function Parent(props) {
    const [items, setItems] = useState([]);

    function getItems () {
        console.log(items); // always prints "[]" 
    }

    useEffect(() => {
        thirdPartyApiCall().then((fetchedItems) =>
            setItems(newItems);
        );
    }, []);

    return newItems.map(item => <Child key={item.id} id={item.id} getItems={getItems}/>)
}

2
Balavishnu V J 10 mars 2019 à 18:18