代码之家  ›  专栏  ›  技术社区  ›  David Trinh

reactjs:无法映射状态,因为它未定义/错误

  •  1
  • David Trinh  · 技术社区  · 6 年前

    我无法描绘我的状态,传给我的孙子 EmailDetails . 我可以控制台将其记录到,直到尝试在 电子邮件详细信息 .

     import React, { Component } from "react";
        import IssueBox from "./issuebox.js";
        import "./App.css";
    
        class App extends Component {
          constructor(props) {
            super(props);
    
            this.state = {
              isLoaded: false,
              emails: [],
              counter: 0,
              title: "Test run",
              value: "",
              selectedEmailId: 0,
              currentSection: "inbox"
            };
          }
    
          componentDidMount() {
            fetch("blah")
              .then(res => res.json())
              .then(result => {
                const emails = result.data;
                console.log("resutl state: ", emails);
                let id = 0;
                for (const email of emails) {
                  email.id = id++;
                }
                this.setState({
                  isLoaded: true,
                  emails: emails
                });
              });
          }
    
          handleClickReproducibleCounter(e) {
            let count = this.state.increment
              ? this.state.count + 1
              : this.state.count - 1;
    
            let increment = this.state.increment;
    
            if (count === 0) {
              increment = true;
            } else if (count >= 2) {
              increment = false;
            }
    
            this.setState({
              count,
              increment
            });
          }
    
          render() {
            return (
              <div className="App">
                <div>
                  emails={this.state.emails}
                  selectedEmailId={this.state.selectedEmailId}
                  onEmailSelected={id => {
                    this.openEmail(id);
                  }}
                  handleClickReproducibleCounter={this.handleClickReproducibleCounter}
                </div>
              </div>
            );
          }
        }
    

    //数据如下:

    [
      {
        groupID: "65da6a",
        urls: [
          {
            id: 85,
            searchedurl: "https://www.yahoo.com",
            errorurl: "https://www.yahoo.com/error505",
            count: 1,
            reproducible: false,
            reproducible_counter: 0
          },
          {
            id: 84,
            searchedurl: "https://www.gmail.com",
            errorurl: "https://www.gmail.com/error404",
            count: 1,
            reproducible: false,
            reproducible_counter: 0
          }
        ]
      },
      {
        groupID: "d4127e",
        urls: [
          {
            id: 3,
            searchedurl: "agwscc",
            errorurl: "xyqa",
            count: 1,
            reproducible: false,
            reproducible_counter: 0,
            resolved: null
          }
        ]
      }
    ];
    

    import React, { Component } from "react";
    import "./App.css";
    import EmailDetails from "./emailDetails";
    
    class IssueBox extends Component {
      constructor(args) {
        super(args);
      }
    
      render() {
    
        const currentEmail = this.props.emails.find(
          x => x.id === this.props.selectedEmailId
        );
        console.log("emailDetail view: ", currentEmail); // email obj is present
    
        return (
          <div className="wrapper">
            <div className="inbox-container">
              <EmailList
                emails={this.props.emails}
                onEmailSelected={this.props.onEmailSelected}
                selectedEmailId={this.props.selectedEmailId}
              />
    
              <EmailDetails
                email={currentEmail} 
                onReproducibleCounter={this.props.handleClickReproducibleCounter}
                onValidateSumbit={this.props.handleClickValidateSumbission}
                handleTextInputChange={this.props.handleTextInputChange}
              />
            </div>
          </div>
        );
      }
    }
    
    export default IssueBox;
    

    //电子邮件详细信息

    import React, { Component } from "react";
    import "./App.css";
    
    class EmailDetails extends Component {
      constructor(args) {
        super(args);
    
        this.state = {
          counter: "",
          email: []
        };
      }
    
      componentDidUpdate(prevProps) {
        if (this.props.email !== prevProps.email) {
          this.setState({ email: this.props.email });
        }
      }
    
      render() {
        const currentEmail = this.state.email;
        console.log(currentEmail, this.state.email.urls); // able to console obj
        const test = this.state.email.urls.map(obj => {
          //error occurs here
          console.log(obj);
        });
        return (
          <div className="email-content">
            <div className="email-content__header">
              <h3 className="email-content__subject">Issue</h3>
              <div className="email-content__from">Group:</div>
            </div>
            <div className="email-content__message">
              <table role="table">
                <thead role="rowgroup">
                  <tr role="row">
                    <th role="columnheader">Searched URL</th>
                    <th role="columnheader">Error URL</th>
                    <th role="columnheader">NR</th>
                    <th role="columnheader">Task</th>
                  </tr>
                </thead>
                <tbody role="rowgroup" className="group-row" />
              </table>
            </div>
    
          </div>
        );
      }
    }
    
    export default EmailDetails;
    

    以此为出发点: codepen

    2 回复  |  直到 6 年前
        1
  •  1
  •   devserkan    6 年前

    我本来打算写一篇评论,但时间变长了,所以我决定提供一个答案。

    遵循你的代码有点困难,但是我看到了一些问题,还有一些我不明白的地方。

    handleClickReproducibleCounter 在里面 App . 所以,你不能用 this 用于回调函数。要么在构造函数中绑定它,要么改用箭头函数。

    handleClickReproducibleCounter = (e) => {
    ...
    }
    

    其次,您在 EmailDetails 组件类型:

    <button onClick={this.props.onReproducibleCounter(obj.id)}>
    

    你为什么要用 this.props ,而且你正在破坏 onReproducibleCounter 已经有道具了。另外,您没有使用回调 onClick 处理程序,立即用参数调用函数,而这并不是您真正想要的。

    <button onClick={() => onReproducibleCounter(obj.id)}>
    

    不过,在JSX props中使用箭头函数或绑定函数并不是很好,因为它们是在每次呈现中重新创建的。但是,在你的情况下,改变这种逻辑有点棘手。您正在将一个道具传递给子组件并在其中映射一些内容。简单的方法是,在父组件中映射,然后将单个项传递给子组件。

    现在,我不明白的事情。你路过一些 obj.id 打电话给你,但是等着 e

    我不明白的第二件事是在你的地图上你用了第二个参数 再现计数器

    email.urls.map((obj, onReproducibleCounter) => {
    

    第二个参数是 index 在这里。

    评论后更新

    您正在使用异步作业在父级中获取此数据。当你试图绘制地图时 this.state.email.urls ,当时没有 然后你会得到一个错误。

    (this.state.email.urls || [] ).map(obj => {
          //error occurs here
        console.log(obj);
    });
    

    所以,如果没有 email.urls 然后我们的地图将使用一个空数组,这对我们来说是可以的。获取完成后,组件将被重新呈现,它将映射真实数据。

    但是,不要把你的道具塞进孩子的状态。使用 email 撑住。如果需要使用它,请在子组件中使用它。

    下面是一个非常简单而愚蠢的例子:

    const Child = (props) => {
      const doSomething = () => {
        const newNumber = props.number * 20;
        props.handleNumber( newNumber );
      }
      return (
        <div>
        <button onClick={doSomething}>Click me, so do something in the Child and change this number.</button>
        </div>
      );
    }
    
    class App extends React.Component {
      state = {
        number: 10,
      }
    
      handleNumber = number => this.setState({number});
    
      render() {
        return <div>
        <p>Number is: {this.state.number}</p>
        <Child number={this.state.number} handleNumber={this.handleNumber} />
        </div>;
      }
    }
    
    ReactDOM.render(<App />, document.getElementById("root"));
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
    <div id="root"></div>
        2
  •  0
  •   Robbie Milejczak Diego Felipe    6 年前

    希望我理解你的要求,当你像这样给孩子们回电时,你需要确保 this

    最简单的方法是 fat arrow

     handleClickReproducibleCounter(e) {
    

    变成

     handleClickReproducibleCounter = e => {
    

    你也可以在 constructor 通过添加一行

    this.handleClickReproducibleCounter = this.handleClickReproducibleCounter.bind(this)
    

    这样做的目的是确保 handleClickReproducibleCounter this.state 总是指父组件的状态(或它绑定到的任何组件)。

    至于你的问题:

    对,你不需要。只要回调正确绑定到父组件状态,并且可以在应用程序中的任何位置更改该状态,则可以根据需要向下传递回调。