在本机应用程序启动时,将初始状态从API调用传递到createStore

时间:2018-05-13 11:31:50

标签: react-native redux react-redux redux-thunk

当我的react-native应用程序启动时,我无法初始化我的redux-state。我需要在应用程序启动之前进行api调用以检索数据以保持我的状态。我喜欢将此调用的结果传递给Provider JSX元素中的createStore函数。 我已经阅读了有关如何做到这一点的不同内容,但它们似乎都不起作用。

这是我的根App组件:

import React, { Component } from 'react';
import { View } from 'react-native';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import reducers from './reducers';
import RouterComponent from './Router';

class App extends Component {
  render() {
    return (
      <Provider store={createStore(reducers, {}, applyMiddleware(ReduxThunk))}>
        <View style={{ flex: 1 }}>
          <RouterComponent />
        </View>
      </Provider>
    );
  }
}


export default App;

我已阅读并尝试了不同的策略: - 在随后的api调用回调中包装render方法的return语句 - 在componentWillMount或componentDidMount

中进行调用

这些都不适合我。当react-native应用程序启动时,从API调用传递createStore初始状态的标准方法是什么。

3 个答案:

答案 0 :(得分:1)

在API调用返回之前,您不能(也不应该)延迟组件的安装(可能甚至会失败)。

您可以在等待API调用返回时显示加载屏幕,方法是检查某个Redux状态(将由API结果填充)在组件中是否仍为空(条件呈现)。

如果要将整个Redux状态替换为API结果,则需要编写根减速器,请参阅答案here

要在应用启动时启动API调用并在成功时填充状态,您可以将以下内容添加到定义/导入Redux store的任何位置:

fetch(...).then(result => store.dispatch(...))

您可以查看persisting it with the client是否适合您的用例,而不是从服务器填充Redux状态。

答案 1 :(得分:1)

最好使用server-rendering

<强> counterApp.js

export const counterApp = (state = {}, action) => {
    switch (action.type) {
        default:
            return state;
    }
}

<强> server.js

//this route will get called for initial page load or page refresh.

server.get('/', (req, res) => {

  const counter = parseInt(20, 10) || 0 
  // demo state,It can be function calling database operation or API.
  let preloadedState = { counter } ; 
  const store = createStore(counterApp, preloadedState);

  const html = renderToString(
    <Provider store={store}>
      <App />
    </Provider>
  );

  const finalState = store.getState()

  res.send(renderFullPage(html, finalState))
});

RenderFullPage:

function renderFullPage(html, preloadedState) {
  return `
    <!doctype html>
    <html>
      <head>
        <title>Redux Universal Example</title>
      </head>
      <body>
        <div id="root">${html}</div>
        <script>
          // WARNING: See the following for security issues around embedding JSON in HTML:
          // http://redux.js.org/recipes/ServerRendering.html#security-considerations
          window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}
        </script>
        <script src="/static/bundle.js"></script>
      </body>
    </html>
    `
}

App.js 中,(仅在客户端进行渲染。)

在App.js中,您可以使用window.__APP_INITIAL_STATE__

访问初始状态
render(<App {...window.__APP_INITIAL_STATE__} />, document.getElementById('root'));

对于服务器端渲染,您必须设置webpack,否则您更喜欢。

答案 2 :(得分:0)

我会通过在状态中设置加载值然后在ComponentDidMount()中请求数据来解决此问题。加载后,将this.state.loaded设置为true,并使用API​​返回的数据呈现商店。没有必要这样做,但它会为客户端提供良好的用户体验,并防止不必要地重新呈现RouterComponent两次。

您是否决定设置errorloaded值,此处的想法是使用ComponentDidMount方法获取数据并使用新数据更新App.state - 这将导致组件重新呈现并将数据应用于新的Store

import React, { Component } from 'react';
import { View } from 'react-native';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import ReduxThunk from 'redux-thunk';
import reducers from './reducers';
import RouterComponent from './Router';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      initialState: {},
      loaded: false,
      error: false
    }
  }

  componentDidMount() {
    // Perform your API call, using which ever library or method you choose, i prefer axios so will demonstrate with this:
    axios.get('path/to/api')
      .then(res => {
        // Send the response to state, which will cause the component to re-render and create the store with the new initialState
        this.setState({
          initialState: res.data,
          loaded: true
        });
      })
      .catch(err => {
        console.error('Error initiating application. Failed to retrieve data from API')
        this.setState({error: true});
      });
  }

  render() {
    // This would be completely optional, but I would show some form of loading icon or text whilst you wait for the API to fetch the data.
    if(!this.state.loaded) {
      return "Loading";
    }

    // If there was an error getting the data, tell the client
    else if(this.state.error) {
      return "Error loading data from API. Please reload the application or try again.";
    }

    // If all is well, the component should render the store with the correct initialState
    else {
      return (
        <Provider store={createStore(reducers, this.state.initialState, applyMiddleware(ReduxThunk))}>
          <View style={{ flex: 1 }}>
            <RouterComponent />
          </View>
        </Provider>
      );
    }
  }
}


export default App;

我希望这会有所帮助。