【问题标题】:Cannot render child component props even though they are being passed properly即使正确传递子组件道具,也无法渲染它们
【发布时间】:2017-05-30 17:23:10
【问题描述】:

我对此感到困惑,我无法弄清楚我做错了什么。我正在尝试做一个维基百科查看器(如Freecodecamp.com 上的解释),基本上是对维基百科文章的简单搜索。

我已经在 HTML/CSS/JS(jQuery) 中完成了它,现在因为我正在学习 React,我正在尝试在 React 中重新创建它。

我已经拆分了我的组件,我正在使用 fetch 使用 Wikipedia API 获取数据,并且所有内容都在呈现,除了搜索结果。我只是无法显示最终结果,这是整个“应用程序”的重点。

使用 React 开发工具,我已经确定正在获取数据并将道具传递给子组件,并且我已经测试了虚拟子组件是否会渲染并且它会渲染,因此渲染所有组件都不是问题,我只是在某处遗漏了一些步骤来让它用所有必要的数据呈现该组件。

所有代码都在this 代码沙箱中。我已经按照我在本地设置的方式进行了设置,这样你就可以清楚地看到我是如何做事的。

感谢任何帮助!

抱歉,问题有点长,包含所有文本和代码。如果这对某些人来说更方便,我还包括了所有按文件拆分的代码。

App.jsx

import React from 'react';
import WikiHeader from './Header';
import WikiSearch from './Search';
import OutputList from './OutputList';
import 'purecss';
import 'purecss/build/grids-responsive-min.css'
import './App.css';

var resultsArray = [];
const wikiAPI = "https://en.wikipedia.org/w/api.php?&";
var wikiHeaders = {
  action: "query",
  format: "json",
  origin: "*",
  prop: "info|extracts",
  exsentences: 2,
  exlimit: 5,
  generator: "search",
  inprop: "url",
  gsrsearch: "",
  gsrnamespace: 0,
  gsrlimit: 5,
  exintro: 1
};

// Format headers to be used for a query string
function toQueryString(obj) {
  var parts = [];
  for (var i in obj) {
    if (obj.hasOwnProperty(i)) {
      parts.push(encodeURIComponent(i) + "=" + encodeURIComponent(obj[i]));
    }
  }
  return parts.join("&");
}

class App extends React.Component {

  constructor() {
    super();

    this.state = {
      results: []
    }

    this.ajaxCall = this.ajaxCall.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  // AJAX using fetch
  ajaxCall() {
    resultsArray = []; // clear array every time ajax is called so that it doesn't just add new items to previous items
    toQueryString(wikiHeaders);

    fetch(wikiAPI + toQueryString(wikiHeaders)).then(function(response) {
      return response.json();
    }).then(function(json) {
      for (var prop in json.query.pages) {

        // push response items to array so that state isn't directly changed (not changing state recommended by Facebook)
        resultsArray.push({
          title: json.query.pages[prop].title,
          url: json.query.pages[prop].fullurl,
          extract: json.query.pages[prop].extract
        });
      }
    }).catch(function(ex) {
      console.log('Json parsing failed', ex); // show error if JSON parsing fails
    });

    this.setState({results: resultsArray}); // set state.results to resultsArray
  }

  handleChange(e) {
    wikiHeaders.gsrsearch = e.target.value; // get input value and store it
  }

  handleSubmit(e) {
    e.preventDefault();
    console.log("Hey, this is a submit event!"); // show if function is being called

    // Call fetch
    this.ajaxCall();

    // Clear the search box after submit
    document.getElementById("search-input").value = "";
  }


  render() {
    return (
      <div className="full-wrapper pure-g">
        <div className="pure-u-1">
          <WikiHeader />
          <WikiSearch onChange={this.handleChange} onSubmit={this.handleSubmit} />
          <OutputList results={this.state.results} />
        </div>
      </div>
    );
  }
}

export default App;

Header.jsx

import React from 'react';

class WikiHeader extends React.Component {
  render() {
    return (
      <h1 className="site-title">Wikipedia Viewer</h1>
    );
  }
};

export default WikiHeader;

搜索.jsx

import React from 'react';

class WikiSearch extends React.Component {

  render() {
    return (
      <div className="search-input">
        <form className="pure-form" onSubmit={this.props.onSubmit}>
          <div className="pure-u-1 pure-u-md-3-4">
            <input type="text" id="search-input" onChange={this.props.onChange} className="pure-input pure-u-1" placeholder="Input your search term here..."></input>
          </div>
          <div className="pure-u-1 pure-u-md-1-4 button-wrapper">
            <button type="submit" id="search-button" className="pure-button pure-button-primary pure-u-1 pure-u-md-23-24">Search</button>
          </div>
        </form>
        <a href="https://en.wikipedia.org/wiki/Special:Random" alt="Open a Random Wikipedia article" target="_blank" rel="noopener noreferrer">or see a random Wikipedia article</a>
      </div>
    );
  }

};

export default WikiSearch;

输出列表.jsx

import React from 'react';
import OutputItem from './OutputItem';

class OutputList extends React.Component {
  render() {

    var finalResult = this.props.results.map((result, index) => {
      return (
        <OutputItem
          key={index}
          title={result.title}
          url={result.url}
          extract={result.extract}
        />
      );
    });

    return (
      <div id="search-output" className="pure-g">
        <ul className="article-list pure-u-1">
          {finalResult}
        </ul>
      </div>
    );
  }
}

export default OutputList;

OutputItem.jsx

import React from 'react';

class OutputItem extends React.Component {
  render() {
    return (
      <div>
        <li className="pure-u-1">
          <a href={this.props.url} target="_blank" rel="noopener noreferrer">
            <h2>{this.props.title}</h2>
            {this.props.extract}
          </a>
        </li>
      </div>
    );
  }
}

export default OutputItem;

【问题讨论】:

  • 在 App.jsx 中,ajax 调用和 setState 方法都是异步的。在进行 ajax 获取时,调用 setState 方法,传入 original 空 resultsArray,因为 ajax 获取尚未完成。将 this.setState 行移动到 ajax fetch 的第一个 'then' 中,就在 for 循环之后。 'this' 将不再被定义,所以只需将 then 中的函数更改为箭头函数。由于可能重复,未发布为答案:stackoverflow.com/questions/36648245/…
  • 这里是编辑后的代码,移动了 setState 函数和 promise 的 thens 转换为箭头函数:codesandbox.io/s/g5Q6MZlRG
  • @thenormalsquid 啊哈就是这样。我知道 ajax 和 setState 都是异步的,我知道我必须在 ajax 之后设置 setState,将它放在第一个“then”中,但我忘记了箭头函数。我想我必须复习一下关于“this”关键字及其上下文的知识。我遇到了您发布的答案,但我并没有真正从那里解决。非常感谢!
  • 没问题!一个很好的理解范围的资源:@​​987654325@ 这是一篇关于箭头函数的很好的帖子:2ality.com/2012/04/arrow-functions.html

标签: javascript reactjs


【解决方案1】:

我想我设法让它在你的沙盒上运行。

this.setState({results: resultsArray}); 在错误的时间完成。它必须等待 AJAX 调用完成。我把它放在你的后面 for (var prop in json.query.pages) { 循环,因此在 ajax 解析回调中。

由此产生的问题是this 不再引用组件实例,因此this.setState 变得未定义。为了避免这个问题,我将).then(function(json) { 变成了一个箭头函数,这样它就不会覆盖this

Here你可以看到我更新的沙盒。

【讨论】:

  • 你和@thenormalsquid 同时回答,都很有帮助。我不知道我是否应该将此标记为答案,因为正常鱿鱼在我的问题上评论的可能重复?如果我将其标记为答案,那么将来有人可能会更容易看到解决方案。
  • 以后不太可能有人来回答您的问题,因为这是一个非常具体的设置,具有非常通用的解决方案。您不妨关闭这个问题 IMO,它不会伤害未来的用户。很高兴我们能够为您提供帮助。
【解决方案2】:

如果我将some logging 添加到您当前的应用程序并运行它,我可以观察到您的finalResults 变量为空,即使查询有效。这是因为由于 Javascript 上下文,fetch 内部的 resultsArray 变量与外部变量不同。

您可以先在 var that = this; 中提升旧上下文,然后在需要执行另一个 setState 时准确地使用它。

https://codesandbox.io/s/KZvXP7kKn

【讨论】:

  • 我想指出,var that = this 现在是一种过时的技术,因为 ES6 箭头函数的出现。否则,在没有 ES6 功能(不是最新版本的浏览器)的情况下,这是一个很好的解决方法。
猜你喜欢
  • 1970-01-01
  • 2021-09-08
  • 2017-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多