我对React redux钩子很陌生。我的目标是在单击按钮时将inputView.js中的数据带入temperatureView.js。
这是我的一些代码,
我在inputView.js
内初始化useSelector
和useDispatch
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内部发送?
如何做到这一点?
编辑
actions/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.log(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 handleChange = (event) => {
const name = event.target.name;
setState({
...state,
[name]: event.target.value,
});
};
const handleChange1 = (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.log('mounted');
return () => console.log('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={handleChange} 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={handleChange1}
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;
问题
- 你不会派遣一个城市来获取温度
- 您立即导航到"/temperatureView";而只是调度的操作仍在处理中,因此
temperatureSelector
的状态尚未更新
解决方案0-获取临时并在完成时导航的调度操作
创建一个全局历史对象,您可以从异步操作中调度导航操作。提交表单后,只需发送操作即可。当获取解析为导航到目标路径时,应用程序将导航到具有路由状态有效载荷的页面。
App.js
使用常规的Router
代替BrowserRouter
,这样您就可以使用自己的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.log(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-调度操作以获取温度并立即导航
调度操作以获取城市温度并立即导航到新页面。该页面被订阅到您的redux商店;等待";以便更新状态。
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.log(err);
});
}
}
温度视图.js
使用useSelector
挂钩访问redux状态。请记住,在获取城市温度并在状态中填充时,要进行任何条件渲染(即"…加载"(。
import React from 'react';
...
function TemperatureView(props) {
const temperatureSelector = useSelector(state => state.temperatureInfo);
return (
...
);
}
将您的操作放入useEffect
,您必须触发操作
useEffect(() => {
dispatch(getTemperature());
}, []);