Передача начального состояния из вызова API в createStore при загрузке реагирующего нативного приложения - PullRequest
0 голосов
/ 13 мая 2018

У меня возникают проблемы с инициализацией моего избыточного состояния при загрузке моего приложения на основе реакции.Мне нужно сделать вызов API, прежде чем приложение загрузится, чтобы получить данные для гидратации моего состояния.Я хотел бы передать результат этого вызова в функцию createStore в моем элементе JSX провайдера.Я читал разные вещи о том, как это сделать, но ни одна из них, похоже, не работает.

Вот мой корневой компонент приложения:

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;

Я прочитал и попробовал разные стратегии: - обертываниеоператор return метода render в тогдашнем обратном вызове вызова API - сделать вызов в componentWillMount или componentDidMount

Ничего из этого не сработало для меня.Каков стандартный способ передачи createStore начального состояния из вызова API при загрузке приложенияact-native.

Ответы [ 3 ]

0 голосов
/ 16 мая 2018

Вы не можете (и не должны) откладывать монтирование компонентов до тех пор, пока не будет возвращен вызов API (он может даже потерпеть неудачу).

Можно отобразить экран загрузки в ожидании возврата вызова API, проверив, является ли определенное состояние Redux (которое будет заполнено результатом API) все еще пустым в вашем компоненте (условное отображение).

Если вы хотите заменить все состояние Redux на ваш результат API, вам нужно написать корневой редуктор, см. Ответ здесь .

Чтобы инициировать вызов API при запуске приложения и заполнить состояние при его успешном выполнении, вы можете добавить следующее в любое место, где Redux store определен / импортирован:

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

Вместо того, чтобы заполнять состояние Redux с сервера, вы можете посмотреть , сохраняя его с клиентом , если это подходит для вашего варианта использования.

0 голосов
/ 16 мая 2018

Лучше использовать 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 или вы предпочитаете.

0 голосов
/ 15 мая 2018

Я бы сделал это, установив значение загрузки в состоянии, а затем запросив данные в ComponentDidMount().После загрузки установите для this.state.loaded значение true и выполните рендеринг хранилища с данными, возвращенными из API.В этом нет необходимости, но это обеспечит хороший UX для клиента и предотвратит ненужный повторный рендеринг RouterComponent дважды.

Независимо от того, решили ли вы установить значения error и loaded, идея состоит в том, чтобы получить данные в методе 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;

Надеюсь, это поможет.

...