如何使用 api 的数据初始化状态

Posted

技术标签:

【中文标题】如何使用 api 的数据初始化状态【英文标题】:How to initialize state with api's data 【发布时间】:2018-06-11 18:06:45 【问题描述】:

我正在创建一个从 api (pokeapi.co) 获取数据的 React/Redux 应用程序。我使用axios 获取数据。当我在反应组件上显示数据时,会导致数据为undefined 的错误。经过一番挖掘,我发现我的状态首先返回初始状态,它是空对象,然后返回 api 数据。但它不会显示在反应中。我是 React 的新手,所以我猜它与 axios 异步功能有关。您如何使用 api 的初始数据设置状态或等待渲染数据直到状态具有 api 的数据?

这里是减速器

function pokemonReducer(state=, action) 
    switch (action.type) 
        case pokemonsActions.GET_POKEMON_SUCCESS:
            
                return  ...state, data: action.payload.data
            

        default:
            
                return state;
            
    


export default pokemonReducer

下面是动作

export const GET_POKEMON_SUCCESS = 'GET_POKEMON_SUCCESS'
export const GET_POKEMON_ERROR = 'GET_POKEMON_ERROR'

function getPokemonSuccess(response) 
    return 
        type: GET_POKEMON_SUCCESS,
        payload: response
    



function getPokemonError(err) 
    return 
        type: GET_POKEMON_ERROR,
        payload: err
    

export function getPokemon() 
    return (disp,getState) => 
             
                return pokeAPI.getPokeAPI()
                              .then((response) =>  disp(getPokemonSuccess(response)))
                              .catch((err)=> disp(getPokemonError(err)))

             

商店

const loggerMiddleware = createLogger()
const middleWare= applyMiddleware(thunkMiddleware,loggerMiddleware);

const store = createStore(rootReducer,preloadedState,
 compose(middleWare, typeof window === 'object' && typeof window.devToolsExtension !== 'undefined'
  ? window.devToolsExtension() : (f) => f
))


const preloadedState=store.dispatch(pokemonActions.getPokemon())

export default store

在 React 组件中

function mapStateToProps(state) 
  return 
    pokemons:state.pokemons
  


    class PokemonAbility extends React.Component 

        render()
            return (
                <div>
                <div className="header">
                  <h1>Fetch Poke Api with axios</h1>
                 </div>
                    <main>
                    <h3> Display pokemons abilities </h3>
                        <p>this.props.pokemons.data.count</p>
                    </main>
                </div>
                )
        
    


    export default connect(
      mapStateToProps
    )(PokemonAbility)

API 数据示例


    "count": 292,
    "previous": null,
    "results": [
        
            "url": "https://pokeapi.co/api/v2/ability/1/",
            "name": "stench"
        ,
        
            "url": "https://pokeapi.co/api/v2/ability/2/",
            "name": "drizzle"
        ,
        
            "url": "https://pokeapi.co/api/v2/ability/3/",
            "name": "speed-boost"
        
    ],
    "next": "https://pokeapi.co/api/v2/ability/?limit=20&offset=20"

【问题讨论】:

这段代码有几种可能会失败。我看到的最有可能的是this.props.pokemons.data.count - 您正在查找data,而从未检查它是否已定义。在您的 ajax 请求完成之前很久,您的组件可能正在安装和渲染。 if(!this.props.pokemons.data) return &lt;div&gt;loading&lt;/div&gt;; 。另一个问题是 pokemon 的复数形式是 pokemon :P 编辑:是的,这是我的评估,即组件在请求完成之前加载初始状态,我试过了,但没有用。返回必须返回呈现有效反应元素的错误。有没有办法使用一个反应生命周期功能来使用这个修复......也许是componentWillMount?哈,也许这样真的不行。口袋妖怪是:) 他的成功消息似乎也将响应存储在带有键“data”的状态中,但在他的 mapStateToProps 中,他以 state.pokemons 的形式访问它。也许这是他没有看到他的数据的另一个原因? 【参考方案1】:

您在数据加载之前渲染您的组件。有很多策略可以解决这个问题。不分先后,以下是一些示例:

1。使渲染短路

如果数据不存在,您可以通过返回加载消息来短路渲染:

function mapStateToProps(state) 
    return 
        pokemons:state.pokemons
    

class PokemonAbility extends React.Component 
    render()
        if (!this.props.pokemons.data) 
            return (
                <div>Loading...</div>
            );
        
        return (
            <div>
                <div className="header">
                    <h1>Fetch Poke Api with axios</h1>
                </div>
                <main>
                    <h3> Display pokemons abilities </h3>
                    <p>this.props.pokemons.data.count</p>
                </main>
            </div>
        );
    


export default connect(mapStateToProps)(PokemonAbility);

2。将数据检查提升到父组件

你可以将mapStateToProps移到更高的组件中,或者抽象出视图组件,只在数据准备好时渲染视图:

function mapStateToProps(state) 
    return 
        pokemons:state.pokemons
    

class SomeHigherComponent extends React.Component 
    render()
        return (
            this.props.pokemons.data ?
                <PokemonAbility pokemons=this.props.pokemons /> :
                <div>Loading...</div>
        );
    

3。高阶组件数据检查

您可以将组件包装在“高阶组件”(一个接受组件类并返回组件类的函数)中,以在渲染之前检查该道具是否存在:

function EnsurePokemon(ChildComponent) 
    return class PokemonEnsureWrapper extends React.Component 
        render() 
            return (
                this.props.pokemons.data ?
                    <ChildComponent ...this.props /> :
                    <div>Loading...</div>
            );
        
    

用法:

export default connect(
    mapStateToProps
)(EnsurePokemon(PokemonAbility))

您可以将任何子组件包装在此 EnsurePokemon HOC 中,以确保它在数据加载之前不会呈现。

【讨论】:

起初,它不起作用。仍然产生同样的问题,但是当我在减速器中将状态设置为 null 时,它终于起作用了。谢谢! 啊,如果初始状态是,那么this.props.pokemons 将始终为真(空对象) - 我编辑了答案以改用this.props.pokemons.data

以上是关于如何使用 api 的数据初始化状态的主要内容,如果未能解决你的问题,请参考以下文章

如何测试调用api的函数?

如何在通量存储中异步加载初始状态?

如何使用我使用 fetch API 调用检索到的数据更新 Reactjs 状态?

React Hooks - 将状态设置为初始状态

如何在 React 中使用 fetch() API 来设置状态

使用redux,如何避免在进行新的API调用之前渲染旧的状态数据?