You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native同组件用connect与store报错的解决方案咨询

解决React Redux的"Invariant Violation: Could not find 'store' in context"错误

你遇到的这个错误本质是组件层级顺序搞反了:你用connect()包裹了App组件,但App组件内部才渲染<Provider store={store}>。Redux的connect需要从组件树的上层上下文获取store,而现在被connect的App处于Provider的外部,自然找不到store。

修复步骤:拆分组件层级

我们需要把「提供store的Provider」和「需要连接store的业务组件」分开,让业务组件处于Provider的内部。

  1. 拆分原App组件:把原来的App组件拆成两个部分:

    • 一个根组件(比如叫App),负责渲染Provider并传入store;
    • 原来的业务逻辑部分(改名为AppContent),作为Provider的子组件,这时候再用connect包裹它就能正常获取store了。
  2. 修改代码实现:
    下面是修改后的完整代码,关键改动已标注:

/* eslint-disable global-require */
import React, { Component } from 'react'
import { Provider, connect } from 'react-redux'
import { BackHandler } from 'react-native'
import AppContainer from './eXpand/Components/Navigation';
import { AppLoading } from 'expo'
import * as Font from 'expo-font'
import { Ionicons } from '@expo/vector-icons'
import { cacheAssets, cacheFonts } from './eXpand/Helpers/Defaults/AssetsCaching'
import store from '~/Store/store'
import registerForPushNotificationsAsync from './eXpand/Components/Services/notifications';
// Local Import
import { setUserGsm } from '~/Store/actions';

// 1. 把原来的App组件改名为AppContent,暂时去掉connect
class AppContent extends Component {
  _isMounted = false;
  constructor(props) {
    super(props)
    this.state = {
      isReady: false,
      GSM: null
    }
  }

  async registerForPush() {
    const { status: existingStatus } = await Permissions.getAsync(
      Permissions.NOTIFICATIONS
    );
    let finalStatus = existingStatus;
    if (existingStatus !== 'granted') {
      const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
      finalStatus = status;
    }
    if (finalStatus !== 'granted') {
      return;
    }
    let gsm = await Notifications.getExpoPushTokenAsync();
    if (this._isMounted) {
      this.setState({ GSM: gsm });
      // 修正逻辑:获取到GSM后再调用action,避免传入null
      this.props.setUserGsm(gsm);
    }
  }

  async componentDidMount() {
    this._isMounted = true;
    if (this._isMounted) {
      this.registerForPush()
    }
    BackHandler.addEventListener('hardwareBackPress', this.handleBackButton);
    await Font.loadAsync({
      Roboto: require('native-base/Fonts/Roboto.ttf'),
      Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
      SFUIDisplayBlack: require('^/Fonts/SFUIDisplay-Black.otf'),
      SFUIDisplayBold: require('^/Fonts/SFUIDisplay-Bold.otf'),
      SFUIDisplayHeavy: require('^/Fonts/SFUIDisplay-Heavy.otf'),
      SFUIDisplayLight: require('^/Fonts/SFUIDisplay-Light.otf'),
      SFUIDisplayMedium: require('^/Fonts/SFUIDisplay-Medium.otf'),
      SFUIDisplaySemibold: require('^/Fonts/SFUIDisplay-Semibold.otf'),
      SFUIDisplayThin: require('^/Fonts/SFUIDisplay-Thin.otf'),
      SFUIDisplayUltralight: require('^/Fonts/SFUIDisplay-Ultralight.otf'),
      MYRIADPROBOLD: require('^/Fonts/MyriadPro-Bold.otf'),
      MyriadProBlackSemiCn: require('^/Fonts/MyriadPro-BlackSemiCn.otf'),
      MyriadProBoldSemiExtended: require('^/Fonts/MyriadPro-BoldSemiExtended.ttf'),
      ...Ionicons.font,
      PTSansRegular: require('^/Fonts/PTSans-Regular.ttf'),
      PTSansBold: require('^/Fonts/PTSans-Bold.ttf'),
      PTSansItalic: require('^/Fonts/PTSans-Italic.ttf'),
    })
    this.setState({ isReady: true })
  }

  componentWillUnmount() {
    BackHandler.removeEventListener('hardwareBackPress', this.handleBackButton);
    this._isMounted = false;
  }

  handleBackButton() {
    return true;
  }

  async _loadAssetsAsync() {
    const imageAssets = cacheAssets([require('^/Logos/expanded.png')])
    const fontAssets = cacheFonts({
      Roboto: require('native-base/Fonts/Roboto.ttf'),
      Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
      SFUIDisplayBlack: require('^/Fonts/SFUIDisplay-Black.otf'),
      SFUIDisplayBold: require('^/Fonts/SFUIDisplay-Bold.otf'),
      SFUIDisplayHeavy: require('^/Fonts/SFUIDisplay-Heavy.otf'),
      SFUIDisplayLight: require('^/Fonts/SFUIDisplay-Light.otf'),
      SFUIDisplayMedium: require('^/Fonts/SFUIDisplay-Medium.otf'),
      SFUIDisplaySemibold: require('^/Fonts/SFUIDisplay-Semibold.otf'),
      SFUIDisplayThin: require('^/Fonts/SFUIDisplay-Thin.otf'),
      SFUIDisplayUltralight: require('^/Fonts/SFUIDisplay-Ultralight.otf'),
      MYRIADPROBOLD: require('^/Fonts/MyriadPro-Bold.otf'),
      MyriadProBlackSemiCn: require('^/Fonts/MyriadPro-BlackSemiCn.otf'),
      MyriadProBoldSemiExtended: require('^/Fonts/MyriadPro-BoldSemiExtended.ttf'),
      PTSansRegular: require('^/Fonts/PTSans-Regular.ttf'),
      PTSansBold: require('^/Fonts/PTSans-Bold.ttf'),
      PTSansItalic: require('^/Fonts/PTSans-Italic.ttf'),
    })
    await Promise.all([imageAssets, fontAssets])
  }

  render() {
    if (!this.state.isReady) {
      return (
        <AppLoading startAsync={this._loadAssetsAsync} onFinish={() => this.setState({ isReady: true })} />
      )
    }
    return <AppContainer />
  }
}

// 2. 给AppContent添加connect,现在它处于Provider内部可以正常获取store
const mapStateToProps = (state) => ({
  GSM: state.GSM
});

const mapDispatchToProps = (dispatch) => ({
  setUserGsm: (GSM) => {
    dispatch(setUserGsm(GSM));
  }
});

const ConnectedAppContent = connect(
  mapStateToProps,
  mapDispatchToProps,
)(AppContent);

// 3. 创建新的根组件,渲染Provider并包裹ConnectedAppContent
class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <ConnectedAppContent />
      </Provider>
    )
  }
}

export default App;

额外注意点

  • 我还修正了一个逻辑问题:原来你在componentDidMount里直接调用this.props.setUserGsm(this.state.GSM),但registerForPush是异步的,这时候GSM还是null。现在改成在获取到gsm后直接调用this.props.setUserGsm(gsm),确保传入正确的token。
  • 确保你的store导入路径正确,~/Store/store的别名配置没问题。

内容的提问来源于stack exchange,提问作者Khaled Boussoffara

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:03:16