【问题标题】:Passing initial state from API call to createStore when react native application boots up当反应原生应用程序启动时,将初始状态从 API 调用传递给 createStore
【发布时间】:2018-10-23 05:56:07
【问题描述】:

当我的 react-native 应用程序启动时,我无法初始化我的 redux 状态。我需要在应用程序启动之前进行 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;

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

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

【问题讨论】:

  • 当您拨打componentDidMount 时会发生什么?

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


【解决方案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))
});

渲染全页:

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 或者你喜欢的。

【讨论】:

  • 这个响应没有考虑服务器没有用 JavaScript 实现的情况,因此不能使用 React SSR。
【解决方案2】:

您不能(也不应该)延迟组件的安装,直到 API 调用返回(它甚至可能失败)。

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

如果你想用你的API结果替换整个Redux状态,你需要写一个root reducer,见答案here

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

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

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

【讨论】:

    【解决方案3】:

    我会通过在状态中设置一个加载值然后在ComponentDidMount() 中请求数据来解决这个问题。加载后,将 this.state.loaded 设置为 true 并使用从 API 返回的数据呈现存储。没有必要这样做,但它会为客户端提供良好的 UX,并防止不必要地重新渲染 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;
    

    我希望这会有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-11-15
      • 1970-01-01
      • 2019-03-18
      • 2018-09-29
      • 1970-01-01
      • 2021-01-04
      • 2021-08-17
      相关资源
      最近更新 更多