为啥 useSelector 不包含任何数据?

Posted

技术标签:

【中文标题】为啥 useSelector 不包含任何数据?【英文标题】:Why useSelector does not contain any data?为什么 useSelector 不包含任何数据? 【发布时间】:2021-04-02 15:19:48 【问题描述】:

我是 React-redux-hooks 的新手。我的目标是在单击按钮时将 inputView.js 中的数据带入 temperatureView.js。

这是我的一些代码,

我在inputView.js内部初始化useSelectoruseDispatch

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?或者也许使用调度? 还是我应该在 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);
    )
    


reducers/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;

TemperatureView.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;

【问题讨论】:

我认为需要更多关于你的减速器和动作的信息。 视情况而定。您是否尝试在组件安装时调度以获取温度(供以后使用,即当用户单击某物时)?或者更确切地说,您似乎正在尝试调度一个操作来获取温度,然后在 temperatureSelector 值更新之前立即离开当前页面,存储当前值处于路由状态的任何内容。 您能否更新您的问题以包含此组件的Minimal, Complete, and Reproducible 代码示例您尝试访问路由状态的组件?您能否谈谈您想要和期望代码做什么,以便我们了解您的用例并提供更好的帮助? @DrewReese 我已经用所有代码更新了我的问题。我的目标是在用户单击按钮后我想稍后使用。我想在下一页显示。 @WonGyoSeo 我更新了代码。 【参考方案1】:

把你的动作放在useEffect里面,你必须触发动作

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

【讨论】:

我收到警告,``` React Hook useEffect 缺少依赖项:'dispatch'。要么包含它,要么移除依赖数组 react-hooks/exhaustive-deps``` 你可以忽略警告 忽略警告而不理解为什么不是一个好习惯。【参考方案2】:

问题

    您不会派出一个城市来测量温度。 您会立即导航到“/temperatureView”,而 just 调度的操作仍在处理中,因此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 并且您已经从它链接,因此无需声明函数 asyncawait 获取分辨率。当提取解析和导航可以发生时。以路由状态传递响应对象。

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>
  ...
);

TemperatureView.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);
      );
  

TemperatureView.js

使用 useSelector 钩子访问 redux 状态。请记住在获取城市温度并将其填充到状态时执行任何条件渲染(即“...加载”)。

import React from 'react';
...

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

  return (
    ...
  );

【讨论】:

以上是关于为啥 useSelector 不包含任何数据?的主要内容,如果未能解决你的问题,请参考以下文章

Redux,使用 useSelector 访问 API 对象内的数据

为啥 QSettings 不存储任何内容?

为啥我们要实现标记接口,即使它们不包含任何方法? [复制]

为啥 C 或 C++ 书籍不包含任何有关网络的内容? [关闭]

为啥预建的协议缓冲区不包含任何头文件

为啥当类包含任何参数化构造函数时编译器不提供默认构造函数? [复制]