Redux,使用 useSelector 访问 API 对象内的数据
Redux, access data inside API object with useSelector
我是 React、Redux 和 hooks 的新手,我正在构建一个简单的应用程序。第一个组件是一个搜索栏,你输入一个 pokemon 的名字,它调用一个 API (pokeapi)(我也使用 react-thunk)和 returns 一个包含关于这个 pokemon 的信息的新组件。
我正在构建结果页面组件,我可以 console.log 状态并查看整个对象,但我无法操作对象内部的任何内容。
例如:console.log(pokemonState) returns 整个对象及其嵌套属性 // console.log(pokemonState.name) returns 未定义。
这是我的代码: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 和 Store
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) 中的整个状态而不是特定属性?
感谢您的帮助。
您的代码中有多个错误:
你用数据声明了一个默认状态=空数组
// 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 但你不在你的 reducer 中使用它
dispatch({
type: 'POKEMON_DATA_SUCCESS',
payload: res.data,
pokemonName: pokemon,
});
最后是你的 useSelector select 整个状态 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 组件将能够通过 selecting 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 数据值,它仍然可以那样工作,但很难看。
我是 React、Redux 和 hooks 的新手,我正在构建一个简单的应用程序。第一个组件是一个搜索栏,你输入一个 pokemon 的名字,它调用一个 API (pokeapi)(我也使用 react-thunk)和 returns 一个包含关于这个 pokemon 的信息的新组件。
我正在构建结果页面组件,我可以 console.log 状态并查看整个对象,但我无法操作对象内部的任何内容。
例如:console.log(pokemonState) returns 整个对象及其嵌套属性 // console.log(pokemonState.name) returns 未定义。
这是我的代码: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 和 Store
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) 中的整个状态而不是特定属性?
感谢您的帮助。
您的代码中有多个错误:
你用数据声明了一个默认状态=空数组
// 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 但你不在你的 reducer 中使用它
dispatch({
type: 'POKEMON_DATA_SUCCESS',
payload: res.data,
pokemonName: pokemon,
});
最后是你的 useSelector select 整个状态 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 组件将能够通过 selecting 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 数据值,它仍然可以那样工作,但很难看。