当反应原生应用程序启动时,将初始状态从 API 调用传递给 createStore

Posted

技术标签:

【中文标题】当反应原生应用程序启动时,将初始状态从 API 调用传递给 createStore【英文标题】:Passing initial state from API call to createStore when react native application boots up 【发布时间】: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 时会发生什么? 【参考方案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 s-s-r。【参考方案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;

我希望这会有所帮助。

【讨论】:

以上是关于当反应原生应用程序启动时,将初始状态从 API 调用传递给 createStore的主要内容,如果未能解决你的问题,请参考以下文章

如何将我的 API 中的数据设置为我的反应组件的初始状态?

当状态/道具改变时,反应原生动画部分列表跳到顶部

有没有办法在 useEffect 的反应组件中设置 xstate 机器的状态?

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

在 redux 连接的反应应用程序中未定义初始状态

反应原生初始区域不随状态更新