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

无Redux时React Navigator子屏调用父组件方法及设置页状态同步问题

Hey Adam, let's tackle this props syncing issue for your settings page— I’ve built similar setups with react-native-i18n, react-navigation, and AsyncStorage, so I know exactly where you might be hitting snags. Here’s a step-by-step solution that’ll get your preferences saving and syncing across the app smoothly.

Core Approach

The key is to pass an update callback from your root App.js to the Settings screen via react-navigation params. When the user changes settings, the Settings screen calls this callback, which updates App.js's state and persists the changes to AsyncStorage. This state change will trigger a re-render of your entire app, applying the new preferences immediately.


Step 1: Set Up App.js with State & AsyncStorage Logic

First, we’ll initialize your app’s settings state, load saved preferences on mount, and create a callback to update settings:

import React, { Component } from 'react';
import { AsyncStorage, View } from 'react-native';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import I18n from 'react-native-i18n';
import SettingsScreen from './SettingsScreen';
// Import your other screens here

const Stack = createStackNavigator();

class App extends Component {
  // Initialize default settings
  state = {
    settings: {
      language: 'auto', // Auto-detect by default
      darkMode: false   // Example second setting
    }
  };

  componentDidMount() {
    // Load saved settings when the app starts
    this.loadSavedSettings();
  }

  loadSavedSettings = async () => {
    try {
      const savedSettings = await AsyncStorage.getItem('appSettings');
      if (savedSettings) {
        const parsedSettings = JSON.parse(savedSettings);
        this.setState({ settings: parsedSettings }, () => {
          // Apply the saved language immediately
          this.setAppLanguage(parsedSettings.language);
        });
      }
    } catch (error) {
      console.error('Failed to load settings:', error);
    }
  };

  // Helper to apply language settings to react-native-i18n
  setAppLanguage = (language) => {
    if (language === 'auto') {
      I18n.locale = I18n.currentLocale();
    } else {
      I18n.locale = language;
    }
  };

  // Callback to update settings from the Settings screen
  updateAppSettings = async (newSettings) => {
    try {
      // Save to AsyncStorage first
      await AsyncStorage.setItem('appSettings', JSON.stringify(newSettings));
      // Update App.js state to trigger app-wide re-render
      this.setState({ settings: newSettings }, () => {
        this.setAppLanguage(newSettings.language);
        // Optional: Force a full re-render if needed (rarely required)
        // this.forceUpdate();
      });
    } catch (error) {
      console.error('Failed to save settings:', error);
    }
  };

  render() {
    return (
      <NavigationContainer>
        <Stack.Navigator>
          <Stack.Screen
            name="Settings"
            component={SettingsScreen}
            // Pass the update callback and current settings as navigation params
            initialParams={{
              updateAppSettings: this.updateAppSettings,
              currentSettings: this.state.settings
            }}
          />
          {/* Add your other screens here */}
        </Stack.Navigator>
      </NavigationContainer>
    );
  }
}

export default App;

Step 2: Build the Settings Screen with Prop Handling

Now, the Settings screen will receive the callback and current settings via navigation params. We’ll use react-native-popup-menu for language selection and a Switch for the dark mode example:

import React, { useState, useEffect } from 'react';
import { View, Text, TouchableOpacity, Switch, StyleSheet } from 'react-native';
import { Menu, MenuOptions, MenuOption, MenuTrigger } from 'react-native-popup-menu';
import I18n from 'react-native-i18n';

const SettingsScreen = ({ route }) => {
  // Extract the callback and current settings from navigation params
  const { updateAppSettings, currentSettings } = route.params;
  // Local state to track changes before syncing
  const [settings, setSettings] = useState(currentSettings);

  // Sync local state with App.js state if it updates (e.g., from another screen)
  useEffect(() => {
    setSettings(currentSettings);
  }, [currentSettings]);

  // Handle language selection from the popup menu
  const handleLanguageChange = (language) => {
    const newSettings = { ...settings, language };
    setSettings(newSettings);
    // Call the callback to update App.js state and AsyncStorage
    updateAppSettings(newSettings);
  };

  // Handle dark mode toggle
  const handleDarkModeToggle = () => {
    const newSettings = { ...settings, darkMode: !settings.darkMode };
    setSettings(newSettings);
    updateAppSettings(newSettings);
  };

  return (
    <View style={styles.container}>
      <Text style={styles.title}>{I18n.t('settings.title')}</Text>

      {/* Language Selection Menu */}
      <View style={styles.settingSection}>
        <Text style={styles.settingLabel}>{I18n.t('settings.language')}</Text>
        <Menu>
          <MenuTrigger>
            <Text style={styles.triggerText}>
              {settings.language === 'auto' 
                ? I18n.t('settings.auto') 
                : settings.language === 'en' 
                  ? 'English' 
                  : 'Deutsch'}
            </Text>
          </MenuTrigger>
          <MenuOptions>
            <MenuOption onSelect={() => handleLanguageChange('auto')}>
              <Text>{I18n.t('settings.auto')}</Text>
            </MenuOption>
            <MenuOption onSelect={() => handleLanguageChange('en')}>
              <Text>English</Text>
            </MenuOption>
            <MenuOption onSelect={() => handleLanguageChange('de')}>
              <Text>Deutsch</Text>
            </MenuOption>
          </MenuOptions>
        </Menu>
      </View>

      {/* Dark Mode Toggle */}
      <View style={styles.settingRow}>
        <Text style={styles.settingLabel}>{I18n.t('settings.darkMode')}</Text>
        <Switch
          value={settings.darkMode}
          onValueChange={handleDarkModeToggle}
        />
      </View>
    </View>
  );
};

const styles = StyleSheet.create({
  container: {
    flex: 1,
    padding: 20,
    backgroundColor: '#fff'
  },
  title: {
    fontSize: 20,
    fontWeight: 'bold',
    marginBottom: 30
  },
  settingSection: {
    marginBottom: 30
  },
  settingLabel: {
    fontSize: 16,
    marginBottom: 10
  },
  triggerText: {
    color: '#007AFF',
    fontSize: 16
  },
  settingRow: {
    flexDirection: 'row',
    justifyContent: 'space-between',
    alignItems: 'center'
  }
});

export default SettingsScreen;

Fixing Common Prop Issues

If you’re still having trouble with props:

  • Class Component Settings Screen: If you’re using a class component instead of functional, access params via this.props.route.params.updateAppSettings (make sure to bind methods or use arrow functions).
  • Nested Navigation: If your Settings screen is inside a TabNavigator or nested stack, you don’t need to pass props through every level— just use useRoute() (functional) or this.props.route (class) to access params directly.
  • State Sync: The useEffect in the Settings screen ensures local state stays in sync with App.js’s state, so if settings are updated elsewhere, the screen will reflect changes immediately.

Post-Eject Notes

When you eject from Expo, you’ll want to replace the deprecated AsyncStorage with @react-native-async-storage/async-storage— the API is nearly identical, so you’ll just need to update your imports.


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 10:45:06