求助:React组件在CodePen.io无法正常渲染的问题
React组件在CodePen无法渲染的解决方案
核心问题排查与修复步骤
1. 配置CodePen为ES模块环境
CodePen默认JS环境不支持import语法,需在JS设置中把JavaScript Preprocessor改为Babel,并勾选Enable ES6 Modules选项。
2. 升级React 18渲染API
React 18已废弃ReactDOM.render,改用createRoot,替换原渲染代码:
// 替换原ReactDOM.render部分 const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <Provider store={store}> <ConnectedRandomQuoteComponent /> </Provider> );
3. 修复初始State与Reducer字段不一致问题
初始State无quoteAndAuthor字段,mapStateToProps直接读取会报错,修改如下:
// 初始State添加quoteAndAuthor字段 const initialState = { quoteAndAuthor: { quote: "Let's go", author: "Bob J. Baker" }, backgroundColor: "#f5f5f5" }; // 修改Reducer,避免无payload时覆盖数据 const rootReducer = (state = initialState, action) => { switch (action.type) { case CHANGE_ALL: // 仅当action携带有效数据时更新 if (action.quote && action.author) { return { ...state, quoteAndAuthor: { quote: action.quote, author: action.author }, backgroundColor: action.backgroundColor || state.backgroundColor }; } // 无有效数据时返回原state return state; default: return state; } };
4. 移除Action中的冗余dispatch
第一个空dispatch({ type: CHANGE_ALL })会导致Reducer错误更新,直接删除:
const changeAll = () => { return async (dispatch) => { try { const response = await fetch("https://quotes.rest/quote/random"); const data = await response.json(); const color = getRandomColor(); dispatch({ type: CHANGE_ALL, quote: data.contents.quotes[0].quote, author: data.contents.quotes[0].author, backgroundColor: color }); } catch (error) { console.log(error); } }; };
5. 绑定New Quote按钮点击事件
组件按钮未绑定触发逻辑,添加点击事件:
<button id="new-quote" onClick={this.props.changeAll}>New Quote</button>
6. 统一CDN包来源(可选)
避免混合不同CDN源的兼容性问题,统一使用esm.sh的包:
import React from 'https://esm.sh/react@18.2.0' import ReactDOM from 'https://esm.sh/react-dom@18.2.0' import * as redux from "https://esm.sh/redux@4.2.0"; import reduxThunk from "https://esm.sh/redux-thunk@2.4.2"; import { Provider, connect } from "https://esm.sh/react-redux@8.0.5";
完整修复代码
import React from 'https://esm.sh/react@18.2.0' import ReactDOM from 'https://esm.sh/react-dom@18.2.0' import * as redux from "https://esm.sh/redux@4.2.0"; import reduxThunk from "https://esm.sh/redux-thunk@2.4.2"; import { Provider, connect } from "https://esm.sh/react-redux@8.0.5"; // Initial State const initialState = { quoteAndAuthor: { quote: "Let's go", author: "Bob J. Baker" }, backgroundColor: "#f5f5f5" }; // Actions: Types const CHANGE_ALL = "CHANGE_ALL"; // Action: Functions const changeAll = () => { return async (dispatch) => { try { const response = await fetch("https://quotes.rest/quote/random"); const data = await response.json(); const color = getRandomColor(); dispatch({ type: CHANGE_ALL, quote: data.contents.quotes[0].quote, author: data.contents.quotes[0].author, backgroundColor: color }); } catch (error) { console.log(error); } }; }; const getRandomColor = () => { const randomColor = "#" + Math.random().toString(16).substr(-6); return randomColor; }; // Reducer const rootReducer = (state = initialState, action) => { switch (action.type) { case CHANGE_ALL: if (action.quote && action.author) { return { ...state, quoteAndAuthor: { quote: action.quote, author: action.author }, backgroundColor: action.backgroundColor || state.backgroundColor }; } return state; default: return state; } }; // Store const store = redux.createStore(rootReducer, redux.applyMiddleware(reduxThunk)); // Component class RandomQuoteComponent extends React.Component { constructor(props) { super(props); } componentDidMount() { this.props.changeAll(); } render() { return ( <div id="main-wrapper" style={{ display: "flex", alignItems: "center", justifyContent: "center", height: "100vh", width: "100%", backgroundColor: this.props.backgroundColor }} > <div id="quote-box"> <h2 id="text">{this.props.quote}</h2> <h6 id="author">- {this.props.author}</h6> <button id="new-quote" onClick={this.props.changeAll}>New Quote</button> <a href={`https://twitter.com/intent/tweet?text="${this.props.quote}" - ${this.props.author}`} id="tweet-quote" target="_blank" rel="noopener noreferrer"> <i className="fab fa-twitter"></i> </a> </div> </div> ); } } // Mapping for state and dispatch actions to props const mapStateToProps = (state) => { return { backgroundColor: state.backgroundColor, quote: state.quoteAndAuthor.quote, author: state.quoteAndAuthor.author }; }; const mapDispatchToProps = (dispatch) => { return { changeAll: () => dispatch(changeAll()) }; }; const ConnectedRandomQuoteComponent = connect( mapStateToProps, mapDispatchToProps )(RandomQuoteComponent); // React 18 Render const root = ReactDOM.createRoot(document.getElementById("root")); root.render( <Provider store={store}> <ConnectedRandomQuoteComponent /> </Provider> );
额外需在CodePen的HTML中添加Font Awesome CDN以显示Twitter图标:
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> <div id="root"></div>
内容的提问来源于stack exchange,提问作者sp1d32-m4n
相关产品推荐
相关产品推荐

