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

求助: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 20:05:39