React/Redux - 对app load/init的调度操作

Sho*_*ota 65 reactjs redux

我从服务器获得令牌认证,所以当我最初加载我的Redux应用程序时,我需要向该服务器发出请求以检查用户是否经过身份验证,如果是,我应该获得令牌.

我发现不建议使用Redux核心INIT操作,那么在呈现应用程序之前如何调度操作呢?

Ser*_*iuk 65

您可以在Root componentDidMount方法中调度操作,在render方法中可以验证身份验证状态.

像这样的东西:

class App extends Component {
  componentDidMount() {
    this.props.getAuth()
  }

  render() {
    return this.props.isReady
      ? <div> ready </div>
      : <div>not ready</div>
  }
}

const mapStateToProps = (state) => ({
  isReady: state.isReady,
})

const mapDispatchToProps = {
  getAuth,
}

export default connect(mapStateToProps, mapDispatchToProps)(App)
Run Code Online (Sandbox Code Playgroud)

  • 尝试实施此解决方案时出现此错误`未捕获错误:在“Connect(App)”的上下文或道具中找不到“商店”。要么将根组件包装在 &lt;Provider&gt; 中,要么将“store”作为道具显式传递给“Connect(App)”。` (2认同)

Chr*_*emp 27

对于为此提出的任何解决方案,我都不满意,然后我想到我正在考虑需要渲染的类.如果我刚刚为启动创建了一个类,然后将东西推入componentDidMount方法并让render显示器加载屏幕呢?

<Provider store={store}>
  <Startup>
    <Router>
      <Switch>
        <Route exact path='/' component={Homepage} />
      </Switch>
    </Router>
  </Startup>
</Provider>
Run Code Online (Sandbox Code Playgroud)

然后有这样的事情:

class Startup extends Component {
  static propTypes = {
    connection: PropTypes.object
  }
  componentDidMount() {
    this.props.actions.initialiseConnection();
  }
  render() {
    return this.props.connection
      ? this.props.children
      : (<p>Loading...</p>);
  }
}

function mapStateToProps(state) {
  return {
    connection: state.connection
  };
}

function mapDispatchToProps(dispatch) {
  return {
    actions: bindActionCreators(Actions, dispatch)
  };
}

export default connect(
  mapStateToProps,
  mapDispatchToProps
)(Startup);
Run Code Online (Sandbox Code Playgroud)

然后编写一些redux操作以异步初始化您的应用程序.工作一种享受.


Sho*_*ota 9

更新:这个答案是针对React Router 3的

我使用react-router onEnter props 解决了这个问题.这是代码的样子:

// this function is called only once, before application initially starts to render react-route and any of its related DOM elements
// it can be used to add init config settings to the application
function onAppInit(dispatch) {
  return (nextState, replace, callback) => {
    dispatch(performTokenRequest())
      .then(() => {
        // callback is like a "next" function, app initialization is stopped until it is called.
        callback();
      });
  };
}

const App = () => (
  <Provider store={store}>
    <IntlProvider locale={language} messages={messages}>
      <div>
        <Router history={history}>
          <Route path="/" component={MainLayout} onEnter={onAppInit(store.dispatch)}>
            <IndexRoute component={HomePage} />
            <Route path="about" component={AboutPage} />
          </Route>
        </Router>
      </div>
    </IntlProvider>
  </Provider>
);
Run Code Online (Sandbox Code Playgroud)

  • 只是要清楚反应 - 路由器4不支持onEnter. (9认同)

Jos*_*man 8

这里所有的答案似乎都是创建根组件并将其在componentDidMount中触发的变体。我最喜欢redux的一件事是,它使数据获取与组件生命周期脱钩。我认为没有理由在这种情况下应该有所不同。

如果要将商店导入到根index.js文件中,则可以initScript()在该文件中调度动作创建者(我们称其为),它将在加载任何内容之前触发。

例如:

//index.js

store.dispatch(initScript());

ReactDOM.render(
  <Provider store={store}>
    <Routes />
  </Provider>,
  document.getElementById('root')
);
Run Code Online (Sandbox Code Playgroud)

  • 我对你关于调度的观点说“是”。Redux 并没有说我们必须从 react 组件内部调度操作。Redux 肯定独立于 React。 (3认同)

Mat*_*wig 7

如果您使用的是React Hooks,则一种解决方案是:

useEffect(() => store.dispatch(handleAppInit()), []);
Run Code Online (Sandbox Code Playgroud)

空数组将确保在第一次渲染时仅调用一次。

完整示例:

import React, { useEffect } from 'react';
import { Provider } from 'react-redux';

import AppInitActions from './store/actions/appInit';

function App() {
  useEffect(() => store.dispatch(AppInitActions.handleAppInit()), []);
  return (
    <Provider store={store}>
      <div>
        Hello World
      </div>
    </Provider>
  );
}

export default App;
Run Code Online (Sandbox Code Playgroud)

  • 或者,您可以使用 `import { useDispatch } from "react-redux";` 然后使用 `constdispatch = useDispatch();` 并设置 useEffect 来调用 `dispatch` 请参阅 https://react-redux.js.org/ api/hooks#useddispatch (3认同)

And*_*dru 5

使用redux-saga中间件你可以做得很好。

只需定义一个 saga,它在被触发之前不监视已调度的动作(例如 withtaketakeLatest)。当fork像这样从 root saga ed 时,它将在应用程序启动时只运行一次。

下面是一个不完整的例子,它需要一些关于redux-saga包的知识,但说明了这一点:

传奇/launchSaga.js

import { call, put } from 'redux-saga/effects';

import { launchStart, launchComplete } from '../actions/launch';
import { authenticationSuccess } from '../actions/authentication';
import { getAuthData } from '../utils/authentication';
// ... imports of other actions/functions etc..

/**
 * Place for initial configurations to run once when the app starts.
 */
const launchSaga = function* launchSaga() {
  yield put(launchStart());

  // Your authentication handling can go here.
  const authData = yield call(getAuthData, { params: ... });
  // ... some more authentication logic
  yield put(authenticationSuccess(authData));  // dispatch an action to notify the redux store of your authentication result

  yield put(launchComplete());
};

export default [launchSaga];
Run Code Online (Sandbox Code Playgroud)

上述信函的代码launchStartlaunchComplete你应该创建终极版动作。创建这样的操作是一种很好的做法,因为它们可以派上用场,以便在启动或完成时通知状态执行其他操作。

然后你的根传奇应该分叉这个launchSaga传奇:

传奇/ index.js

import { fork, all } from 'redux-saga/effects';
import launchSaga from './launchSaga';
// ... other saga imports

// Single entry point to start all sagas at once
const root = function* rootSaga() {
  yield all([
    fork( ... )
    // ... other sagas
    fork(launchSaga)
  ]);
};

export default root;
Run Code Online (Sandbox Code Playgroud)

请阅读redux-saga非常好的文档以获取更多信息。