React Native上传后自动刷新展示组件问题求助
Hey there! Let's work through this problem together—you're right that Redux feels overkill for a small app, so let's start with lighter, more fitting solutions first, then cover a streamlined Redux guide if you still want to explore that route.
First, let's fix the obvious syntax issues in your current code that are preventing the refresh function from firing:
- In
AddTextModal'suploadmethod, you're referencingthis.props.refreshFlatListItembut not calling it—add parentheses:
upload() { this.props.refreshFlatListItem(); // Call the function! }
- In
onSubmit, you're also not invokingthis.upload—update it tothis.upload();
That said, the core issue is these two components don't share a parent-child relationship, so passing props directly won't work. Here are better alternatives:
一、Lightweight Alternatives to Redux
Option 1: React Navigation Screen Focus Listener (Most Recommended)
For small apps, the simplest fix is to have your VaultScreen reload data whenever it comes into focus. This way, no matter where the upload happens, navigating back to Vault will automatically refresh the content.
Update your VaultScreen like this:
class VaultScreen extends React.Component { constructor(props) { super(props) this.state = { items: [], itemsFormatted: [], photo_id: null, itemSelected: '', searchQuery: '', numberOfUploads: 0 }; this.loadData = this.loadData.bind(this); } componentDidMount() { this.loadData(); // Listen for when the screen comes into focus this.focusListener = this.props.navigation.addListener('focus', () => { this.loadData(); }); } componentWillUnmount() { // Clean up the listener to avoid memory leaks this.focusListener(); } // Wrap your database fetch logic in a reusable function loadData() { db.transaction(tx => { tx.executeSql('SELECT * FROM items', [], (tx, results) => { const newItems = []; for (let i = 0; i < results.rows.length; i++) { newItems.push(results.rows.item(i)); } this.setState({ items: newItems }); // Add logic to format itemsFormatted here if needed }); }); } // You can keep your original refresh function if needed, but loadData does the heavy lifting now refreshFlatListItem = () => { this.loadData(); } // ... rest of your component code }
Option 2: React Context for Global Refresh Signals
If you want to trigger a refresh immediately after upload (without waiting for navigation), use React Context to create a lightweight global signal:
- Create a
RefreshContext.jsfile:
import React, { createContext, useContext, useState, useCallback } from 'react'; const RefreshContext = createContext(); export function RefreshProvider({ children }) { const [vaultRefreshTrigger, setVaultRefreshTrigger] = useState(0); // Function to trigger a refresh by updating the trigger value const triggerVaultRefresh = useCallback(() => { setVaultRefreshTrigger(prev => prev + 1); }, []); return ( <RefreshContext.Provider value={{ vaultRefreshTrigger, triggerVaultRefresh }}> {children} </RefreshContext.Provider> ); } export function useRefresh() { return useContext(RefreshContext); }
- Wrap your app in the provider in
App.js:
import { RefreshProvider } from './RefreshContext'; function App() { return ( <RefreshProvider> <NavigationContainer> {/* Your existing stack navigator code */} </NavigationContainer> </RefreshProvider> ); }
- Update
VaultScreento listen for the refresh trigger:
import { useRefresh } from './RefreshContext'; // Convert VaultScreen to a function component (easier to use hooks) function VaultScreen({ navigation }) { const [items, setItems] = useState([]); const { vaultRefreshTrigger } = useRefresh(); const loadData = () => { db.transaction(tx => { tx.executeSql('SELECT * FROM items', [], (tx, results) => { const newItems = []; for (let i = 0; i < results.rows.length; i++) { newItems.push(results.rows.item(i)); } setItems(newItems); }); }); }; // Reload data whenever the trigger changes useEffect(() => { loadData(); }, [vaultRefreshTrigger]); // ... rest of your component logic }
- Trigger the refresh in
AddTextModal:
import { useRefresh } from './RefreshContext'; // For class components, use contextType class AddTextModal extends React.Component { static contextType = RefreshContext; onSubmit = () => { const { textCaption } = this.state; if (textCaption != null) { this.add(textCaption); // Trigger the refresh this.context.triggerVaultRefresh(); this.setState({ textCaption: null }); this.props.navigation.navigate('Vault'); } }; // ... rest of your component code }
Option 3: Event Emitter Event Bus
Create a global event emitter to send refresh signals between components:
- Create
EventEmitter.js:
import { EventEmitter } from 'events'; const eventEmitter = new EventEmitter(); export default eventEmitter;
- Listen for the event in
VaultScreen:
import eventEmitter from './EventEmitter'; class VaultScreen extends React.Component { componentDidMount() { this.loadData(); // Listen for a refresh event this.refreshListener = eventEmitter.on('refresh-vault', () => { this.loadData(); }); } componentWillUnmount() { // Clean up the listener eventEmitter.removeListener('refresh-vault', this.refreshListener); } // ... loadData function and other code }
- Emit the event in
AddTextModal'sonSubmit:
import eventEmitter from './EventEmitter'; onSubmit = () => { const { textCaption } = this.state; if (textCaption != null) { this.add(textCaption); // Send the refresh signal eventEmitter.emit('refresh-vault'); this.setState({ textCaption: null }); this.props.navigation.navigate('Vault'); } };
二、Streamlined Redux Guide for Your Use Case
If you eventually need to scale your app and want to use Redux, here's a minimal setup tailored to your refresh needs:
1. Install Dependencies
npm install @reduxjs/toolkit react-redux
2. Create Redux Store and Slice
Make a store.js file:
import { configureStore, createSlice } from '@reduxjs/toolkit'; // Slice to handle refresh triggers const refreshSlice = createSlice({ name: 'refresh', initialState: { vaultRefreshTrigger: 0 }, reducers: { triggerVaultRefresh: (state) => { state.vaultRefreshTrigger += 1; } } }); export const { triggerVaultRefresh } = refreshSlice.actions; export const store = configureStore({ reducer: { refresh: refreshSlice.reducer } });
3. Wrap Your App in the Redux Provider
Update App.js:
import { Provider } from 'react-redux'; import { store } from './store'; function App() { return ( <Provider store={store}> <NavigationContainer> {/* Your existing stack navigator code */} </NavigationContainer> </Provider> ); }
4. Update VaultScreen to Listen for Redux State Changes
import { useSelector } from 'react-redux'; import { useEffect, useState } from 'react'; function VaultScreen({ navigation }) { const [items, setItems] = useState([]); const vaultRefreshTrigger = useSelector(state => state.refresh.vaultRefreshTrigger); const loadData = () => { db.transaction(tx => { tx.executeSql('SELECT * FROM items', [], (tx, results) => { const newItems = []; for (let i = 0; i < results.rows.length; i++) { newItems.push(results.rows.item(i)); } setItems(newItems); }); }); }; useEffect(() => { loadData(); }, [vaultRefreshTrigger]); // ... rest of your component code }
5. Dispatch the Refresh Action in AddTextModal
import { connect } from 'react-redux'; import { triggerVaultRefresh } from './store'; class AddTextModal extends React.Component { onSubmit = () => { const { textCaption } = this.state; if (textCaption != null) { this.add(textCaption); // Dispatch the refresh action this.props.triggerVaultRefresh(); this.setState({ textCaption: null }); this.props.navigation.navigate('Vault'); } }; // ... rest of your component code } // Connect the component to Redux to access the action export default connect(null, { triggerVaultRefresh })(AddTextModal);
内容的提问来源于stack exchange,提问作者James Stirrat

