Redux组件间数据传递与共享问题咨询(新手)
Hey there! Let's break down how to fix your component data sharing issue with Redux—this is a super common pain point for beginners, so you're not alone! Based on your setup, the core problem is likely that your components aren't properly syncing with the Redux store to share the "selected link" state. Here's a step-by-step solution tailored to your project structure:
First, let's define a clear, modular state shape that separates concerns between navigation and content. This makes it easier to track and update shared data:
// Example initial state (in your root reducer) const initialState = { navigation: { links: [], isLoading: false, error: null }, content: { selectedLinkId: null, // This is the shared state key! htmlContent: "", isLoading: false, error: null } };
You need two critical action types: one to update the selected link in the store, and others to handle API calls for links and content.
NavigationActions.js
Add an action to trigger link selection:
// Sync action to set selected link export const selectLink = (linkId) => ({ type: "SELECT_LINK", payload: linkId }); // Async actions for fetching links (use redux-thunk for async logic) export const fetchLinksRequest = () => ({ type: "FETCH_LINKS_REQUEST" }); export const fetchLinksSuccess = (links) => ({ type: "FETCH_LINKS_SUCCESS", payload: links }); export const fetchLinksFailure = (error) => ({ type: "FETCH_LINKS_FAILURE", payload: error }); // Async thunk to fetch links from API export const fetchLinks = () => (dispatch) => { dispatch(fetchLinksRequest()); fetch("/api/your-links-endpoint") .then((res) => res.json()) .then((links) => dispatch(fetchLinksSuccess(links))) .catch((err) => dispatch(fetchLinksFailure(err.message))); };
ContentActions.js
Add actions to fetch content based on the selected link:
// Async actions for content export const fetchContentRequest = () => ({ type: "FETCH_CONTENT_REQUEST" }); export const fetchContentSuccess = (html) => ({ type: "FETCH_CONTENT_SUCCESS", payload: html }); export const fetchContentFailure = (error) => ({ type: "FETCH_CONTENT_FAILURE", payload: error }); // Async thunk to fetch content by link ID export const fetchContent = (linkId) => (dispatch) => { if (!linkId) return; dispatch(fetchContentRequest()); fetch(`/api/content/${linkId}`) .then((res) => res.text()) // Since you're rendering HTML .then((html) => dispatch(fetchContentSuccess(html))) .catch((err) => dispatch(fetchContentFailure(err.message))); };
Make sure your reducers correctly update the store state (remember: reducers are pure functions—never mutate state directly):
navigationReducer.js
const initialNavigationState = { links: [], isLoading: false, error: null }; export const navigationReducer = (state = initialNavigationState, action) => { switch (action.type) { case "FETCH_LINKS_REQUEST": return { ...state, isLoading: true, error: null }; case "FETCH_LINKS_SUCCESS": return { ...state, isLoading: false, links: action.payload }; case "FETCH_LINKS_FAILURE": return { ...state, isLoading: false, error: action.payload }; default: return state; } };
contentReducer.js
const initialContentState = { selectedLinkId: null, htmlContent: "", isLoading: false, error: null }; export const contentReducer = (state = initialContentState, action) => { switch (action.type) { case "SELECT_LINK": return { ...state, selectedLinkId: action.payload, htmlContent: "" }; case "FETCH_CONTENT_REQUEST": return { ...state, isLoading: true, error: null }; case "FETCH_CONTENT_SUCCESS": return { ...state, isLoading: false, htmlContent: action.payload }; case "FETCH_CONTENT_FAILURE": return { ...state, isLoading: false, error: action.payload }; default: return state; } };
Your containers will act as the bridge between your UI components and the Redux store.
NavigationContainer.js
Connect the Navigation component to dispatch link selection and receive links:
import { connect } from "react-redux"; import { fetchLinks, selectLink } from "../actions/NavigationActions"; import Navigation from "../components/Navigation"; const mapStateToProps = (state) => ({ links: state.navigation.links, isLoading: state.navigation.isLoading, error: state.navigation.error }); const mapDispatchToProps = (dispatch) => ({ loadLinks: () => dispatch(fetchLinks()), onLinkClick: (linkId) => dispatch(selectLink(linkId)) }); export default connect(mapStateToProps, mapDispatchToProps)(Navigation);
Then in your Navigation component, call props.onLinkClick(link.id) when a link is clicked.
ContentContainer.js
Connect the Content component to watch for selected link changes and fetch content:
import { useEffect } from "react"; import { connect } from "react-redux"; import { fetchContent } from "../actions/ContentActions"; import Content from "../components/Content"; const ContentContainer = ({ selectedLinkId, fetchContent, htmlContent, isLoading, error }) => { // Fetch content whenever the selected link changes useEffect(() => { fetchContent(selectedLinkId); }, [selectedLinkId, fetchContent]); return <Content htmlContent={htmlContent} isLoading={isLoading} error={error} />; }; const mapStateToProps = (state) => ({ selectedLinkId: state.content.selectedLinkId, htmlContent: state.content.htmlContent, isLoading: state.content.isLoading, error: state.content.error }); const mapDispatchToProps = (dispatch) => ({ fetchContent: (linkId) => dispatch(fetchContent(linkId)) }); export default connect(mapStateToProps, mapDispatchToProps)(ContentContainer);
Don’t forget these easy-to-miss steps:
- Wrap your root component (Home) in Redux's
<Provider>inindex.jsto make the store available to all connected components. - Add
redux-thunkmiddleware to your store setup (required for async actions like API calls). Example:// index.js store setup import { createStore, applyMiddleware, compose } from "redux"; import { Provider } from "react-redux"; import thunk from "redux-thunk"; import rootReducer from "./reducers"; // Combine your navigation and content reducers here const store = createStore( rootReducer, compose( applyMiddleware(thunk), window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__() // For debugging ) ); ReactDOM.render( <Provider store={store}> <Home /> </Provider>, document.getElementById("root") );
内容的提问来源于stack exchange,提问作者Avi

