Почему useSelector не содержит никаких данных?

Я очень новичок в React-redux-hooks. Моя цель - перенести данные из inputView.js в TemperatureView.js, когда я нажимаю кнопку.

Вот часть моего кода,

Я инициализирую useSelector и useDispatch внутри inputView.js

const temperatureSelector=useSelector(state=>state.temperatureInfo)
const dispatch = useDispatch();

...

const handleClick = (e) => {
  e.preventDefault();
  dispatch(getTemperature());  
  history.push({
    pathname: '/temperatureView',
    state: { params: temperatureSelector }
  })
}

Когда я захожу внутрь TemperatureView.js, это ничего не дает. Я неправильно использую useSelector? или, может быть, использовать Dispatch? Или я должен отправить внутри useEffect?

Как это сделать?

РЕДАКТИРОВАТЬ

действия/index.js

export const getTemperature = (city) => {
return async function(dispatch) {
    await fetch("myhttp").then(res => {
        return res.json();
    }).then(resJOSN => {
        dispatch({type: "GET_TEMPERATURE", payload: resJOSN})
    }).catch(err => {
        console.info(err);
    })
    
}
}

редукторы/index.js

import { combineReducers } from 'redux';
const temperatureinfo = (state = {
temperatureinfo:{}
}, action) => {
if (action.type === "GET_TEMPERATURE") {
    state = {...state, temperatureInfo:action.payload}
}
return state
}
const reducers = combineReducers ({
temperatureInfo:temperatureinfo,
})
export default reducers;

index.js

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './view/App';
import reportWebVitals from './reportWebVitals';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import reducers from './reducers';
import thunk from 'redux-thunk';

ReactDOM.render(
  <Provider store = {createStore(reducers, applyMiddleware(thunk))}><App /></Provider>,
    
  document.getElementById('root')
);

reportWebVitals();

App.js

import TemperatureView from './TemperatureView';
import InputView from './InputView';

function App(props) {

  return (
    <div>
      <BrowserRouter>
        <Route path = "/" exact component = {InputView} />
        <Route path = "/temperatureView" component = {TemperatureView}/>
      </BrowserRouter>
    </div>
  );
}

export default App;

InputView.js

    import React, {useEffect} from 'react';
import { makeStyles } from '@material-ui/core/styles';

import InputLabel from '@material-ui/core/InputLabel';
import MenuItem from '@material-ui/core/MenuItem';
import Select from '@material-ui/core/Select';
import FormControl from '@material-ui/core/FormControl';
import Input from '@material-ui/core/Input';
import Button from '@material-ui/core/Button';
import { useHistory } from "react-router-dom";
import { useSelector, useDispatch } from 'react-redux';
import { getTemperature } from '../actions';

const useStyles = makeStyles((theme) => ({
  root: {
    '& > *': {
      margin: theme.spacing(1),
    },
  },
}));

function InputView(props) {

  const history = useHistory();
  const classes = useStyles();
  const City =  [
    {"id": "YG", "name": 'Yangon'},
    {"id": "NY", "name": 'New York'}
  ];

  const [state, setState] = React.useState({
    apiKey: '123',
    cityName: 'NY'
  });

   const temperatureSelector=useSelector(state=>state.temperatureInfo)
   const dispatch = useDispatch();

  const handleChangeEvent = (event) => {
    const name = event.target.name;
    setState({
      ...state,
      [name]: event.target.value,
    });
  };

  const handleChangeEvent1 = (event) => {
    const name = event.target.name;
    setState({
      ...state,
      [name]: event.target.value,
    });
  };
  
  const handleClick = (e) => {
    e.preventDefault();
    dispatch(getTemperature());  
    history.push({
      pathname: '/temperatureView',
      state: { params: temperatureSelector }
    })
    
  }

  useEffect(()=>{
    console.info('mounted');
    return () => console.info('unmounting...');
    },[]
  );

  return (
    <div style = {{display: "flex", height:"100vh", alignItems:"center", justifyContent:"center" }}> 
        <form className = {classes.root} noValidate autoComplete = "off">
            <div style = {{marginTop:"40px", marginBottom:"40px"}}>
                <FormControl>
                    <InputLabel htmlFor = "input-form" style = {{color:"red"}}>Your API Key</InputLabel>
                    <Input id = "input-form" value = {state.apiKey} name = "apiKey" onChange = {handleChangeEvent} style = {{width:"300px"}} />
                </FormControl>
            </div>
            <div style = {{marginTop:"40px", marginBottom:"40px"}}>
                <FormControl className = {classes.formControl}>
                    <InputLabel shrink id = "button-file" style = {{color:"red"}}>
                    City Name
                    </InputLabel>
                    <Select
                        labelId = "button-file"
                        id = "button-file"
                        value = {state.cityName}
                        displayEmpty
                        name = "cityName"
                        onChange = {handleChangeEvent1}
                        style = {{width:"300px"}}
                    >
                        <MenuItem key = {City[0].id} value = {City[0].id}>
                        {City[0].name}
                        </MenuItem>
                        <MenuItem key = {City[1].id} value = {City[1].id}>
                        {City[1].name}
                        </MenuItem>
                    </Select>
            </FormControl>
            </div>
        <div style = {{marginTop:"100px", marginBottom:"100px"}}>
        <Button type = "submit" htmlFor = "button-file" variant = "contained" color = "secondary" fullWidth = {true} onClick = {handleClick} >Submit</Button>
        </div>
      </form>
    </div>
  );
}

export default InputView;

Температурный вид.js

    import React from 'react';
import Typography from '@material-ui/core/Typography';
import TextField from '@material-ui/core/TextField';

function TemperatureView(props) {
    return <div style = {{marginTop: "80px", marginLeft: "40px"}}> 
        <Typography color = "error" >
            Celcius
        </Typography>
        <TextField
          id = "celcius"
          defaultValue = "23.5"
          InputProps = {{
            readOnly: true,
          }}
        />
        <Typography color = "error" >
            Fahrenheit
        </Typography>
        <TextField
          id = "fahrenheit"
          defaultValue = "2234"
          InputProps = {{
            readOnly: true,
          }}
        />
    </div>
}

export default TemperatureView;

Я думаю, что нужно больше информации о вашем редукторе и действиях.

Won Gyo Seo 24.12.2020 05:03

Зависит от. Вы пытаетесь отправить, чтобы получить температуру, когда компонент монтируется (для использования позже, т.е. когда usr что-то нажимает)? Или, скорее, кажется, что вы пытаетесь отправить действие, чтобы получить температуру, а затем сразу же уйти с текущей страницы до того, как значение temperatureSelector обновится, сохраняя все текущее значение в состоянии маршрута.

Drew Reese 24.12.2020 05:03

Можете ли вы обновить свой вопрос, включив в него минимальный, полный и воспроизводимый пример кода этого компонента и компонента, в котором вы пытаетесь получить доступ к состоянию маршрута? Можете ли вы рассказать, что вы хотите и ожидаете от кода, чтобы мы могли понять ваш вариант использования и предоставить лучшую помощь?

Drew Reese 24.12.2020 05:11

@DrewReese Я уже обновил свой вопрос со всем кодом. моя цель - я хочу использовать позже, после того, как пользователь нажмет кнопку. Я хочу отобразить на следующей странице.

Safiah Nadiah 24.12.2020 05:41

@WonGyoSeo Я обновил коды.

Safiah Nadiah 24.12.2020 05:42
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Навигация по приложениям React: Исчерпывающее руководство по React Router
Навигация по приложениям React: Исчерпывающее руководство по React Router
React Router стала незаменимой библиотекой для создания одностраничных приложений с навигацией в React. В этой статье блога мы подробно рассмотрим...
Массив зависимостей в React
Массив зависимостей в React
Все о массиве Dependency и его связи с useEffect.
0
5
719
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Поместите свои действия внутрь useEffect, вы должны вызвать действия

 useEffect(() => {
   dispatch(getTemperature());  
  }, []);

Я получил предупреждение: «React Hook useEffect имеет недостающую зависимость: 'dispatch'. Либо включите его, либо удалите массив зависимостей react-hooks/exhaustive-deps```

Safiah Nadiah 24.12.2020 05:54

вы можете игнорировать предупреждение

Anil Loutombam 24.12.2020 07:24

Игнорировать предупреждения без понимания причин — плохая привычка.

Drew Reese 24.12.2020 07:37
Ответ принят как подходящий

Проблемы

  1. Вы не отправляете город, чтобы получить температуру.
  2. Вы немедленно переходите к «/ TemperatureView», пока только что отправленное действие все еще обрабатывается, поэтому temperatureSelector еще не обновился в состоянии.

Решение 0. Действие отправки, которое извлекает временную информацию и перемещается по завершении

Создайте объект глобальной истории, в который вы можете отправлять действия навигации из ваших асинхронных действий. Когда форма отправлена, просто отправьте действие. Когда выборка разрешается, выполняется навигация по целевому пути, и приложение переходит на страницу с полезной нагрузкой состояния маршрута.

App.js

Вместо BrowserRouter используйте обычный Router, чтобы вы могли использовать свой собственный объект history. Создайте объект history из пакета history.

import { Router } from 'react-router-dom';
import { createBrowserHistory } from 'history';
...

export const history = createBrowserHistory()

function App(props) {

  return (
    <div>
      <Router history = {history}>
        ...
      </Router>
    </div>
  );
}

действие

fetch возвращает Promise, и вы уже выполняете цепочку из него, поэтому нет необходимости объявлять функцию async и await разрешение выборки. Когда выборка разрешается и может происходить навигация. Передайте объект ответа в состоянии маршрута.

import { history } from '/path/to/app.js';

export const getTemperature = (city) => {
  return function(dispatch) {
    fetch("myhttp")
      .then(res => res.json())
      .then(resJOSN => {
        dispatch({ type: "GET_TEMPERATURE", payload: resJOSN });
        history.push({
          pathname: '/temperatureView',
          state: {
            temperatureSelector: resJOSN,
          },
        });
      })
      .catch(err => {
        console.info(err);
      });
  }
}

InputView.js

Обновите обработчик, чтобы передать действие state.cityName в getTemperature.

const handleClick = (e) => {
  e.preventDefault(); // <-- prevent default form action
  dispatch(getTemperature(state.cityName)); // <-- pass city
}

Исправьте кнопку отправки формы. Он уже находится в форме, поэтому обработчик onClick не требуется. Обработчик должен быть прикреплен к обработчику onSubmit формы.

return (
  ...
    <form
      className = {classes.root}
      noValidate autoComplete = "off"
      onSubmit = {handleClick}
    >
      ...    
    
      <Button
        type = "submit" // <-- will submit the form when clicked!
        variant = "contained"
        color = "secondary"
        fullWidth = {true}
      >
        Submit
      </Button>
    </form>
  ...
);

Температурный вид.js

Получите доступ к состоянию маршрута с помощью хука useLocation.

import React from 'react';
import { useLocation } from 'react-router-dom';
...

function TemperatureView(props) {
  const { state: { temperatureSelector } } = useLocation(); // <-- access route state

  return (
    ...
  );
}

Решение 1. Отправьте действие для получения временной папки и немедленной навигации

Действие Dispatch для получения температуры в городе и немедленного перехода на новую страницу. Эта страница подписана на ваш магазин избыточности и «ждет» обновления состояния.

InputView.js

Те же обновления, что и выше, но также и навигация. Удалите строку temperatureSelector, она будет доступна в другом месте.

const handleClick = (e) => {
  e.preventDefault(); // <-- prevent default form action
  dispatch(getTemperature(state.cityName)); // <-- pass city
  history.push('/temperatureView'); // <-- navigate immediately
}

действие

Те же изменения, что и выше, но без импорта history и без действий по навигации.

export const getTemperature = (city) => {
  return function(dispatch) {
    fetch("myhttp")
      .then(res => res.json())
      .then(resJOSN => {
        dispatch({ type: "GET_TEMPERATURE", payload: resJOSN });
      })
      .catch(err => {
        console.info(err);
      });
  }
}

Температурный вид.js

Получите доступ к состоянию редукции с помощью хука useSelector. Не забудьте выполнить любой условный рендеринг (например, «... загрузка»), пока городская температура извлекается и заполняется в состоянии.

import React from 'react';
...

function TemperatureView(props) {
  const temperatureSelector = useSelector(state => state.temperatureInfo);

  return (
    ...
  );
}

Другие вопросы по теме