Я очень новичок в 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;
Зависит от. Вы пытаетесь отправить, чтобы получить температуру, когда компонент монтируется (для использования позже, т.е. когда usr что-то нажимает)? Или, скорее, кажется, что вы пытаетесь отправить действие, чтобы получить температуру, а затем сразу же уйти с текущей страницы до того, как значение temperatureSelector
обновится, сохраняя все текущее значение в состоянии маршрута.
Можете ли вы обновить свой вопрос, включив в него минимальный, полный и воспроизводимый пример кода этого компонента и компонента, в котором вы пытаетесь получить доступ к состоянию маршрута? Можете ли вы рассказать, что вы хотите и ожидаете от кода, чтобы мы могли понять ваш вариант использования и предоставить лучшую помощь?
@DrewReese Я уже обновил свой вопрос со всем кодом. моя цель - я хочу использовать позже, после того, как пользователь нажмет кнопку. Я хочу отобразить на следующей странице.
@WonGyoSeo Я обновил коды.
Поместите свои действия внутрь useEffect
, вы должны вызвать действия
useEffect(() => {
dispatch(getTemperature());
}, []);
Я получил предупреждение: «React Hook useEffect имеет недостающую зависимость: 'dispatch'. Либо включите его, либо удалите массив зависимостей react-hooks/exhaustive-deps```
вы можете игнорировать предупреждение
Игнорировать предупреждения без понимания причин — плохая привычка.
temperatureSelector
еще не обновился в состоянии.Создайте объект глобальной истории, в который вы можете отправлять действия навигации из ваших асинхронных действий. Когда форма отправлена, просто отправьте действие. Когда выборка разрешается, выполняется навигация по целевому пути, и приложение переходит на страницу с полезной нагрузкой состояния маршрута.
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 (
...
);
}
Действие 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 (
...
);
}
Я думаю, что нужно больше информации о вашем редукторе и действиях.