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

如何在React Router v4路由间共享应用状态(仅使用React)

Hey there! Let's tackle this problem—since you're sticking to plain React without state management libraries, the key here is leveraging React's built-in patterns: lifting state up and passing updater functions (either via props or React Context if your component tree gets deep). Let's walk through both approaches with clear code examples.

Approach 1: Pass Updater Functions via Props (Best for Shallow Component Trees)

This is the simplest pattern when your RestaurantPage isn't nested too deep under MyFoodApp. You'll define an updater function in MyFoodApp that modifies the restaurant state immutably, then pass that function down to RestaurantPage as a prop.

Step 1: Define State and Updater in MyFoodApp

import { useState } from 'react';
import RestaurantPage from './RestaurantPage';

function MyFoodApp() {
  // Initial state with sample restaurants
  const [restaurants, setRestaurants] = useState([
    { id: 1, name: "Joe's Pizza", reviews: [] },
    { id: 2, name: "Mama's Pasta", reviews: [] }
  ]);

  // Immutable updater function: finds the target restaurant and adds a new review
  const addReviewToRestaurant = (restaurantId, newReview) => {
    setRestaurants(prevRestaurants => 
      prevRestaurants.map(restaurant => {
        if (restaurant.id === restaurantId) {
          // Return a NEW restaurant object with updated reviews (no direct mutation!)
          return {
            ...restaurant,
            reviews: [...restaurant.reviews, newReview]
          };
        }
        return restaurant; // Leave other restaurants unchanged
      })
    );
  };

  return (
    <div className="app-container">
      {/* Pass the target restaurant and updater function to RestaurantPage */}
      <RestaurantPage 
        restaurant={restaurants.find(r => r.id === 1)} // Example: pass restaurant with ID 1
        onAddReview={(newReview) => addReviewToRestaurant(1, newReview)}
      />
    </div>
  );
}

export default MyFoodApp;

Step 2: Use the Updater in RestaurantPage

Now RestaurantPage can call the passed function to trigger a state update in MyFoodApp:

import { useState } from 'react';

function RestaurantPage({ restaurant, onAddReview }) {
  const [reviewText, setReviewText] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!reviewText.trim()) return;

    // Create a new review object (customize fields as needed)
    const newReview = {
      id: Date.now(), // Simple unique ID using timestamp
      text: reviewText,
      date: new Date().toLocaleDateString()
    };

    // Call the parent's updater function
    onAddReview(newReview);
    setReviewText(''); // Clear input after submission
  };

  if (!restaurant) return <div>Restaurant not found</div>;

  return (
    <div className="restaurant-page">
      <h1>{restaurant.name}</h1>
      <h3>Customer Reviews</h3>
      <ul>
        {restaurant.reviews.map(review => (
          <li key={review.id}>
            <p>{review.text}</p>
            <small>{review.date}</small>
          </li>
        ))}
      </ul>

      <form onSubmit={handleSubmit}>
        <textarea
          value={reviewText}
          onChange={(e) => setReviewText(e.target.value)}
          placeholder="Share your experience..."
          rows="4"
        />
        <button type="submit">Post Review</button>
      </form>
    </div>
  );
}

export default RestaurantPage;

Approach 2: Use React Context (Best for Deep Component Trees)

If RestaurantPage is nested several levels down (leading to "prop drilling"), React Context lets you share state and updater functions across components without passing props through every level.

Step 1: Create a Restaurant Context

First, make a dedicated context file to hold your state logic:

// RestaurantContext.js
import { createContext, useContext, useState } from 'react';

// Create the context
const RestaurantContext = createContext();

// Provider component to wrap your app
export function RestaurantProvider({ children }) {
  const [restaurants, setRestaurants] = useState([
    { id: 1, name: "Joe's Pizza", reviews: [] },
    { id: 2, name: "Mama's Pasta", reviews: [] }
  ]);

  const addReviewToRestaurant = (restaurantId, newReview) => {
    setRestaurants(prevRestaurants => 
      prevRestaurants.map(restaurant => {
        if (restaurant.id === restaurantId) {
          return { ...restaurant, reviews: [...restaurant.reviews, newReview] };
        }
        return restaurant;
      })
    );
  };

  // Pass state and updater functions to the context provider
  return (
    <RestaurantContext.Provider value={{ restaurants, addReviewToRestaurant }}>
      {children}
    </RestaurantContext.Provider>
  );
}

// Custom hook to access the context easily
export function useRestaurants() {
  return useContext(RestaurantContext);
}

Step 2: Wrap MyFoodApp with the Provider

import { RestaurantProvider } from './RestaurantContext';
import RestaurantPage from './RestaurantPage';

function MyFoodApp() {
  return (
    <RestaurantProvider>
      <div className="app-container">
        {/* Now you don't need to pass state/props directly */}
        <RestaurantPage restaurantId={1} />
      </div>
    </RestaurantProvider>
  );
}

export default MyFoodApp;

Step 3: Access Context in RestaurantPage

Use the custom hook to get state and the updater function:

import { useState } from 'react';
import { useRestaurants } from './RestaurantContext';

function RestaurantPage({ restaurantId }) {
  const { restaurants, addReviewToRestaurant } = useRestaurants();
  const restaurant = restaurants.find(r => r.id === restaurantId);
  const [reviewText, setReviewText] = useState('');

  if (!restaurant) return <div>Restaurant not found</div>;

  const handleSubmit = (e) => {
    e.preventDefault();
    if (!reviewText.trim()) return;

    const newReview = {
      id: Date.now(),
      text: reviewText,
      date: new Date().toLocaleDateString()
    };

    addReviewToRestaurant(restaurantId, newReview);
    setReviewText('');
  };

  return (
    <div className="restaurant-page">
      <h1>{restaurant.name}</h1>
      <h3>Customer Reviews</h3>
      <ul>
        {restaurant.reviews.map(review => (
          <li key={review.id}>
            <p>{review.text}</p>
            <small>{review.date}</small>
          </li>
        ))}
      </ul>

      <form onSubmit={handleSubmit}>
        <textarea
          value={reviewText}
          onChange={(e) => setReviewText(e.target.value)}
          placeholder="Share your experience..."
          rows="4"
        />
        <button type="submit">Post Review</button>
      </form>
    </div>
  );
}

export default RestaurantPage;

Key Takeaway

No matter which approach you pick, always follow React's immutable state rule: never modify state directly. Use map, spread operators (...), or other immutable methods to create new state objects—this ensures React detects changes and re-renders components correctly.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 07:24:46