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

Posted

技术标签:

【中文标题】Redux,使用 useSelector 访问 API 对象内的数据【英文标题】:Redux, access data inside API object with useSelector 【发布时间】:2021-12-17 16:09:12 【问题描述】:

我是 React、Redux 和 hooks 的新手,我正在构建一个简单的应用程序。第一个组件是一个搜索栏,你输入一个 pokemon 名称,它会调用一个 API (pokeapi)(我也使用 react-thunk)并返回一个包含该 pokemon 信息的新组件。

我正在构建结果页面组件,我可以控制台记录状态并查看整个对象,但我无法操作对象内部的任何内容。

例如:console.log(pokemonState) 返回整个对象及其嵌套属性 // console.log(pokemonState.name) 返回未定义。

这是我的代码:App.js

import  Route, NavLink, Redirect  from 'react-router-dom';
import PokemonSearch from './container/PokemonSearch';
import PokemonResult from './container/PokemonResult';

function App() 
  return (
    <div className="App">
      <nav>
        <NavLink to='/'>Search</NavLink>
      </nav>
      <h1>TEST</h1>
      <Route path='/' exact component=PokemonSearch />
      <Route path='/pokemon/:pokemon' exact component=PokemonResult />
      <Redirect to='/' />
    </div>
  );


export default App;

上面我没有贴,但是顶层的index.js也被Provider包裹了。 PokemonSearch.js

import React,  useState  from 'react';
import  getPokemonData  from '../actions/pokemonAction';
import  useDispatch  from 'react-redux';
import '../styles/PokemonSearch.css';

const PokemonSearch = (props) => 
  const [search, setSearch] = useState('');
  const dispatch = useDispatch();

  const FetchData = () => 
    dispatch(getPokemonData(search));
  ;

  const handleChange = (e) => 
    setSearch(e.target.value);
  ;

  const handleSubmit = (e) => 
    e.preventDefault();
    FetchData();
    props.history.push(`/pokemon/$search`);
  ;

  return (
    <div>
      <div className="bar">
        <form onSubmit=handleSubmit>
          <input
            type="text"
            className="searchInput"
            placeholder="Search a Pokemon"
            onChange=handleChange
          />
        </form>
      </div>
      <div></div>
    </div>
  );
;

export default PokemonSearch;

动作

import axios from 'axios';

export const getPokemonData = (pokemon) => async (dispatch) => 
  try 
    dispatch(
      type: 'POKEMON_DATA_LOADING',
    );

    const res = await axios.get(`https://pokeapi.co/api/v2/pokemon/$pokemon`);

    dispatch(
      type: 'POKEMON_DATA_SUCCESS',
      payload: res.data,
      pokemonName: pokemon,
    );
   catch (e) 
    dispatch(
      type: 'POKEMON_DATA_FAIL',
    );
  
;

减速器

const DefaultState = 
  loading: false,
  data: [],
  errorMsg: '',
;

const PokemonSearchReducer = (state = DefaultState, action) => 
  switch (action.type) 
    case 'POKEMON_DATA_LOADING':
      return 
        ...state,
        loading: true,
        errorMsg: '',
      ;
    case 'POKEMON_DATA_FAIL':
      return 
        ...state,
        loading: false,
        errorMsg: "Error: cannot find the pokemon you're looking for.",
      ;
    case 'POKEMON_DATA_SUCCESS':
      return 
        ...state,
        loading: false,
        errorMsg: '',
        data: 
          ...state.data,
          data: action.payload,
        ,
      ;

    default:
      return state;
  
;

export default PokemonSearchReducer;

Root Reducer 和存储

import  combineReducers  from 'redux';
import PokemonSearchReducer from './PokemonSearchReducer';

const rootReducer = combineReducers(
  PokemonSearch: PokemonSearchReducer,
);

export default rootReducer;

import  createStore  from 'redux';
import  composeWithDevTools  from 'redux-devtools-extension';
import  applyMiddleware  from 'redux';
import thunk from 'redux-thunk';
import rootReducer from './reducers/rootReducer';

const Store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(thunk))
);

export default Store;

PokemonResult.js

import  useSelector  from 'react-redux';

const PokemonResult = (props) => 
  const pokemonState = useSelector((state) => state.PokemonSearch);
  console.log(pokemonState.name);

  return (
    <div>
      <h1>Title</h1>
    </div>
  );
;

export default PokemonResult;

console.log(PokemonState) 输出: what apis return when you type a valid pokemon name

所以我想知道出了什么问题,为什么我可以 console.log 整个状态而不是我的组件 (PokemonResult) 中的特定属性?

感谢您的帮助。

【问题讨论】:

你能把console.log(pokemonState)的输出放上来吗? 嗨 Nicolas,这是输出的屏幕截图:screenshot link 请修剪您的代码,以便更容易找到您的问题。请按照以下指南创建minimal reproducible example。 【参考方案1】:

您的代码中有多个错误:

你用 data = empty array 声明一个默认状态

// use lowerCamelCase for variable, it can be confusing as PascalCase is used for class / function component
const DefaultState = 
  loading: false,
  data: [],
  errorMsg: '',
;

那么在你的 reducer 中,数组现在是一个名为 data 的对象,其中包含一个数据对象 (?) data: data: yourpayload , ...

    case 'POKEMON_DATA_SUCCESS':
      return 
        ...state,
        loading: false,
        errorMsg: '',

        data: 
          ...state.data,
          data: action.payload,
        ,
      ;

然后在您的操作 getPokemonData 中,您使用 pokemonName 发送 POKEMON_DATA_SUCCESS 但您不在减速器中使用它

dispatch(
    type: 'POKEMON_DATA_SUCCESS',
    payload: res.data,
    pokemonName: pokemon,
);

最后你的 useSelector 选择整个状态 pokemonSearch 这样你就可以返回 loading: boolean, errorMsg: string, data: data: pokemon

import  useSelector  from 'react-redux';

const PokemonResult = (props) => 
  const pokemonState = useSelector((state) => state.PokemonSearch); // <== if you want to select pokemonData it should be state.PokemonSearch.data.data
  console.log(pokemonState.name); // <=== it should be pokemonState.data.data.name

  return (
    <div>
      <h1>Title</h1>
    </div>
  );
;

export default PokemonResult;

为了使其更清洁并按预期工作,您可以更改在减速器的 POKEMON_DATA_SUCCESS 中处理有效负载的方式:

    case 'POKEMON_DATA_SUCCESS':
      // spreading state is useless here since you change all the state
      return 
        loading: false,
        errorMsg: '',
        data: action.payload,
      ;

因此,您的 PokemonResult 组件将能够通过选择 PokemonSearch 状态中的数据来读取 pokemon 名称:

import  useSelector  from 'react-redux';

const PokemonResult = (props) => 
  const pokemonState = useSelector((state) => state.PokemonSearch.data);
  console.log(pokemonState.name);

  return (
    <div>
      <h1>Title</h1>
    </div>
  );
;

export default PokemonResult;

不要忘记更改您的 defaultState 数据值,它仍然可以这样工作,但它很难看。

【讨论】:

非常感谢 Nicolas 的帮助,它现在有效,我理解我的错误。老实说,感谢您花时间写这么详细的解释。我真的很感激! @FriedrichGLX 您可以检查勾选并为答案投票,这样人们就知道您不再需要帮助,如果您还有任何问题,请不要犹豫:) 谢谢,刚刚勾选了答案。但是我还不能投票,只要我有足够的声望点就会这样做

以上是关于Redux,使用 useSelector 访问 API 对象内的数据的主要内容,如果未能解决你的问题,请参考以下文章

如何在类组件中使用 react-redux useSelector?

在Redux中使用useSelector和useDispatch

在Redux中使用useSelector和useDispatch

当输入字段已经使用 useState() 映射时,如何使用 useSelector() 连接 redux 存储

测试 react-redux useSelector

关于使用 Redux 工具包的 `useSelector` 和 `createSelector` 的混淆