Как обновить React UI из состояния в магазине Redux с помощью useEffect - PullRequest
1 голос
/ 02 мая 2020

У меня есть следующий функциональный компонент. Я сделал отладку и прочитал несколько постов о реакции-редуксе и использовании эффекта, но все еще не увенчался успехом. При первоначальном рендеринге состояние в моем хранилище с избыточностью равно нулю, но затем изменяется, отражая новое состояние с данными Тем не менее, мой реагирующий пользовательский интерфейс не отражает это. Я понимаю, в чем проблема, но я не знаю точно, как это исправить. Я мог бы поступать неправильно, получая данные из моего обновленного состояния в хранилище избыточностей.

Вот мой компонент:

const Games = (props) => {
    const [gamesData, setGamesData] = useState(null)
    const [gameData, setGameData] = useState(null)
    const [gameDate, setGameDate] = useState(new Date(2020, 2, 10))    
    const classes = GamesStyles()

    // infinite render if placed in 
    // useEffect array
    const {gamesProp} = props
    useEffect(() => {

        function requestGames() {
            var date = parseDate(gameDate)            
            try {                
                props.getGames(`${date}`)
                // prints null, even though state has changed
                 console.log(props.gamesProp)
                setGamesData(props.gamesProp)

            } catch (error) {
                console.log(error)
            }
        }    
        requestGames()
    }, [gameDate])


    // data has not been loaded yet
    if (gamesData == null) {
        return (
            <div>
                <Spinner />
            </div>
        )
    } else {
        console.log(gamesData)

        return (
           <div><p>Data has been loaded<p><div>
           {/* this is where i would change gameDate */}
        )
    }
}

const mapStateToProps = (state) => {
    return {
        gamesProp: state.gamesReducer.games,
    }
}

const mapDispatchToProps = (dispatch) => {
    return {
        getGames: (url) => dispatch(actions.getGames(url)),
    }
}
export default connect(mapStateToProps, mapDispatchToProps)(Games)

Вот мой редуктор

import {GET_GAMES} from '../actions/types'


const initialState = {
    games: null // what we're fetching from backend
}

export default function(state = initialState, action){
    switch(action.type){

        case GET_GAMES:
            // prints correct data from state
            //console.log(action.payload)

            return{
                ...state,                 
                games: action.payload
            }

        default:
            return state
    }
}

Вот мое действие

import axios from 'axios'
import {GET_GAMES} from './types'


// all our request go here

// GET GAMES
export const getGames = (date) => dispatch => {

    //console.log('Date', date)
    axios.get(`http://127.0.0.1:8000/games/${date}`)
    .then(res => {

        dispatch({            
            type: GET_GAMES,
            payload: res.data
        })
    }).catch(err => console.log(err))

}

my state after initial render

Когда я помещаю реквизиты из состояния в мой массив зависимостей для useEffect, состояние обновляется, но приводит к бесконечной визуализации, поскольку реквизиты меняются. Это происходит, даже если я уничтожу реквизит.

Вот изображение моего состояния редукса после его обновления при первоначальном рендере.

1 Ответ

1 голос
/ 02 мая 2020

У вас возникли проблемы, потому что вы пытались установить состояние на основе данных, которые были в замыкании . props.gamesProp в пределах useEffect, который вы имели, никогда не будет обновляться, даже если родительские данные изменились.

Причина, по которой props.gamesProp был нулевым в эффекте, заключается в том, что при каждом рендеринге ваш компонент по существу имеет новый экземпляр props, поэтому, когда запускается useEffect, версия props, которую видит внутренняя часть useEffect, соответствует тому, что существовало при этом рендере.

Любая функция внутри компонента, включая обработчики событий и эффекты, «видит ”Реквизит и состояние от рендера, в котором он был создан.

https://reactjs.org/docs/hooks-faq.html#why -am-i-vision-stale-props-or-state-inside-my-function

Если вам не нужно изменять gamesState внутри вашего компонента, я настоятельно рекомендую вам не дублировать реквизит в состояние.

Я бы также рекомендовал использовать useDispatch и useSelector вместо подключения для функциональных компонентов.

Вот некоторые модификации вашего компонента, основанные на том, что я вижу в нем в настоящее время, и что я ' мы только что описали:

import { useDispatch, useSelector } from 'react-redux';

const Games = (props) => {
  const gamesData = useSelector((state) => state.gamesReducer.games);
  const dispatch = useDispatch();
  const [gameData, setGameData] = useState(null);
  const [gameDate, setGameDate] = useState(new Date(2020, 2, 10));
  const classes = GamesStyles();

  // infinite render if placed in
  // useEffect array
  useEffect(() => {
    const date = parseDate(gameDate);
    try {
      dispatch(actions.getGames(`${date}`));
    } catch (error) {
      console.log(error);
    }
  }, [gameDate, dispatch]);

  // data has not been loaded yet
  if (gamesData == null) {
    return (
      <div>
        <Spinner />
      </div>
    );
  } else {
    console.log(gamesData);

    return (
      <div>
        <p>Data has been loaded</p>
      </div>
      // this is where i would change gameDate
    );
  }
};

export default Games;

Если вам нужно получить свое состояние из реквизита, вот что говорит React Documentation на крючках:

https://reactjs.org/docs/hooks-faq.html#how -do- я орудие-getderivedstatefromprops

...