Sommaire

J'ai 2 états : isLogged et counter. isLogged est un booléen où l'utilisateur peut le définir sur vrai ou faux, ce faisant, l'utilisateur peut accéder à certaines pages où si isLogged est défini sur vrai. Le counter est juste un simple int où l'utilisateur peut l'incrémenter/décrémenter. Les deux états sont enregistrés sur sessionStorage si leurs valeurs sont modifiées ; et lors de l'initialisation, j'ai exécuté une expédition pour obtenir les deux valeurs du sessionstorage. La valeur par défaut pour isLogged est false et counter est 0.

Le problème

isLogged = true
counter = 4

Si j'essaie d'appeler une action increment, les valeurs seront désormais les suivantes :

isLogged = false
counter = 5

Comme vous pouvez le voir, il réinitialise le isLogged à sa valeur par défaut. Cependant, lorsque je regarde le panneau sessionstorage sur Chrome, il est indiqué : {isLogged: true, counter: 5}, il enregistre donc toujours les valeurs sur sessonstorage et tout semble fonctionner correctement. Cependant, sur l'application elle-même, il est indiqué que isLogged est désormais false et que l'utilisateur ne peut plus accéder à certaines pages même s'il ne s'est pas déconnecté pour une raison quelconque.

Ce qui est bizarre, c'est que si je rafraîchis la page, les états sont maintenant :

isLogged = true
counter = 5

(bien sûr, les données ont été récupérées du sessionstorage) Mais si je passe maintenant le isLogged à false, cela réinitialisera également le counter à 0 :

isLogged = false
counter = 0

Ainsi, le problème est que lorsque j'appelle une répartition/action pour modifier un certain état, tous les autres états sont réinitialisés.

Codes

src/actions/index.js

export const increment = (num) => {
    return {
        type: 'INCREMENT',
        payload: num
    }
}

export const decrement = () => {
    return {
        type: 'DECREMENT'
    }
}

export const get_data = () => {
    return {
        type: 'GET'
    }
}

export const signin = () => {
    return {
        type: 'SIGN_IN'
    }
}    

export const signout = () => {
    return {
        type: 'SIGN_OUT'
    }
}

export const getlogged = () => {
    return {
        type: 'GET'
    }
}

src/reducers/counter.js

const counterReducer = (state = 0, action) => {
    switch(action.type) {
        case 'INCREMENT':
            state += action.payload
            saveCounter(state)
            return state
        case 'DECREMENT':
            state -= 1
            saveCounter(state)
            return state
        case 'GET':
            state = getCounter()
            return state
        default:
            return 0
    }
}

const saveCounter = (state) => {
    const data = {
        counter: state
    }
    sessionStorage.setItem("data", JSON.stringify(data))
}

const getCounter = () => {
    if (sessionStorage.getItem("data") != null) {
        const data = JSON.parse(sessionStorage.getItem("data"))
        return data["counter"]
   }
    else {
        return 0
    }
}

export default counterReducer

src/reducers/isLogged.js

const loggedReducer = (state = false, action) => {
    switch(action.type) {
        case 'SIGN_IN':
            state = true
            saveLogged(state)
            return state
        case 'SIGN_OUT':
            state = false
            saveLogged(state)
            return state
        case 'GET':
            state = getLogged()
            return state
        default:
            return false
    }
}

const saveLogged = (state) => {
    const data = {isLogged: state}
    sessionStorage.setItem("logged", JSON.stringify(data))
}

const getLogged = () => {
    if (sessionStorage.getItem("logged") != null) {
        const data = JSON.parse(sessionStorage.getItem("logged"))
        return data["isLogged"]
    }
    else {
        return false
    }

}

export default loggedReducer

Je l'appelle sur un composant comme celui-ci :

useEffect(() => {
    getCounter()
}, [])

const counter = useSelector(state => state.counter)
const dispatch = useDispatch()

const getCounter = () => {
    dispatch(get_data())
}

return (
    <div className="container-fluid mt-3">
        <p className="text-white">Counter: {counter}</p>
        <button onClick={() => dispatch(increment(4))} className="btn btn-primary">+</button>
        <button onClick={() => dispatch(decrement())} className="btn btn-danger">-</button>
    </div>
)
0
Dran Dev 11 nov. 2020 à 18:03

1 réponse

Meilleure réponse

Le problème est que dans vos deux réducteurs, vous retournez l'état par défaut dans le cas par défaut. Cela signifie que chaque action non gérée réinitialisera les états.

Par exemple, lorsque vous envoyez une action d'incrémentation, elle passe à l'état par défaut dans votre réducteur et définit l'état sur false.

Le cas par défaut doit simplement renvoyer l'état inchangé.


En plus de cela, useEffect semble un peu dangereux, car getCounter change à chaque rendu, il sera appelé à chaque fois.

Je vous conseillerais également d'utiliser un middleware pour le redux si vous souhaitez enregistrer quelque chose dans le localStorage. Un réducteur est censé être une fonction sans effets secondaires donc vous ne respectez pas la règle ici. Vous ne devez pas non plus lire à partir du localStorage via une action mais plutôt lire à partir du localStorage lors de la création de votre magasin.

1
Axnyff 11 nov. 2020 à 15:19