React项目中状态内GitHub仓库数组交互与渲染异常问题求解
问题根因定位
核心逻辑错误
- 代码完全混淆了异步请求的执行顺序,存在两个致命问题:
get_repos方法发起的axios请求是异步执行的,代码不会等响应返回再往下走:执行流程是先初始化一个空数组repo_list,发起请求后立刻把这个空数组return出去赋值给this.state.repos。等很久之后axios拿到响应,只是往这个初始的空数组引用里push了仓库数据,但全程没有调用React的setState方法,根本不会触发组件重渲染。- 观察到的控制台打印异常,刚好能佐证这个异步问题:
- 直接打印
this.state.repos时,Chrome控制台保存的是这个数组的内存引用,不会立刻做内容快照。等你展开控制台查看的时候,axios已经返回响应,数组被push了所有仓库数据,所以你看着好像打印的是有内容的数组。 - 用模板字符串拼接
Repos: ${this.state.repos}时,字符串转换是代码执行那一刻同步完成的——这个时候axios还没返回,数组是空的,转换后的空字符串会被立刻快照输出,所以你永远看不到仓库内容。
- 直接打印
- 热更新时能正常显示、手动刷新/部署后就空白的现象,也完全符合这个逻辑:热更新触发组件重渲染时,之前的axios请求早就返回了,那个存在state里的数组引用已经被填充了内容,所以能正常展示;手动刷新页面时,组件重新初始化,同步的初始渲染流程跑完时,axios请求还没返回,数组是空的,之后即使请求返回了,没有
setState触发重渲染,页面也不会更新。部署到线上后网络延迟更高,请求返回更慢,自然全程显示空白。
其他附带问题
- 在构造函数里发起异步请求是React的反模式,会导致请求时序混乱,数据请求应该统一放在
componentDidMount生命周期中执行。 - Repo组件里把props存进state是冗余操作:只要直接从props读取数据即可,额外存到state里反而会出现props更新但组件不重新渲染的问题。
- 列表项的key不需要在Repo内部重复声明,外层组件传递key时已经完成了列表识别的逻辑,内部重复写key没有实际作用。
修正后的RepoTable组件代码
class RepoTable extends Component { constructor(props) { super(props); // 初始化时repos设为空数组,不要在构造函数内发起异步请求 this.state = { repos: [] } } // 将数据请求放在componentDidMount生命周期中执行 componentDidMount() { axios .get("https://api.github.com/users/Geno1131993/repos") .then((response) => { // 拿到响应后必须调用setState更新state,才能触发组件重渲染 this.setState({ repos: response.data }) }) } render() { // 增加可选链判断,避免初始渲染时数组为空导致map报错 const repo_list = this.state.repos?.map((repo) => { return <Repo key={repo.name} repo={repo} />; }); return ( <ul key="repo_table"> {repo_list} </ul> ); } }
修正后的Repo组件代码
class Repo extends Component { random() { let key = Math.floor((Math.random() * 5) + 1); switch(key){ case 1: return "lotus1"; case 2: return "lotus2"; case 3: return "lotus3"; case 4: return "lotus4"; case 5: return "lotus5"; } return -1; } render() { // 直接从props读取数据,不需要经过state中转 const { repo } = this.props; return ( <li className="repo"> <a href={repo.html_url} rel="noreferrer" target="_blank"> <div className="repo_title">{repo.name}</div> <div className="repo_info"> <p id={this.random()} className="lotus"></p> <p className="repo_description">{repo.description}</p> </div> </a> </li> ); } }
内容的提问来源于stack exchange,提问作者ukka
相关产品推荐
相关产品推荐

