【问题标题】:Converting React class based component to functional component将基于 React 类的组件转换为功能组件
【发布时间】:2020-10-27 05:53:25
【问题描述】:

我正在尝试将以下 sn-p 代码转换为使用基于功能的组件。

class IssueTable extends React.Component {
  constructor() {
    super();
    this.state = { issues: [] };
    setTimeout(() => {
      this.createIssue(sampleIssue);
    }, 2000);
  }

  componentDidMount() {
    this.loadData();
  }

  loadData() {
    setTimeout(() => {
      this.setState({ issues: initialIssues });
    }, 500);
  }

  createIssue(issue) {
    issue.id = this.state.issues.length + 1;
    issue.created = new Date();
    const newIssueList = this.state.issues.slice();
    newIssueList.push(issue);
    this.setState({ issues: newIssueList });
  }

  render() {
    const issueRows = this.state.issues.map(issue =>
      <IssueRow key={issue.id} issue={issue} />
    );

    return (
      <table className="bordered-table">
        <thead>
          <tr>
            <th>ID</th>
            <th>Status</th>
            <th>Owner</th>
            <th>Created</th>
            <th>Effort</th>
            <th>Due Date</th>
            <th>Title</th>
          </tr>
        </thead>
        <tbody>
          {issueRows}
        </tbody>
      </table>
    );
  }
}

在尝试转换它时,表格会以某种方式不断重新渲染,我得到 key not unique 错误。我使用了 useEffect 但我不知道如何使用构造函数之类的功能。如何使用函数来实现它。 (issueRows 是一个处理表中行数据的组件)。功能版本如下图:

function IssueTable(){

  const [issue1, setIssues] = React.useState([]);

  React.useEffect(()=>{loadData()},[]);

    setTimeout(() => {
      createIssue(sampleIssue);
    }, 2000);
  


  function loadData() {
    setTimeout(() => {
      setIssues(issues);
    }, 500);
  }


  function createIssue(issue) {
    issue.id = issue1.length+1;
    issue.created = new Date();
    const newIssueList = issue1.slice();
    newIssueList.push(issue);
    setIssues(newIssueList);
    }
    

    
    const issueRows = issue1.map(issue =>
        <IssueRow key={issue.id} issue={issue} />
      )

    return <table className="bordered-table">
    <thead>
      <tr>
        <th>ID</th>
        <th>Status</th>
        <th>Owner</th>
        <th>Created</th>
        <th>Effort</th>
        <th>Due Date</th>
        <th>Title</th>
      </tr>
    </thead>
    <tbody>
      {issueRows}
    </tbody>
  </table>
}

function IssueRow(props){
    return(
        <tr>
            <td>{props.issue.id}</td>
            <td>{props.issue.status}</td>
            <td>{props.issue.owner}</td>
            <td>{props.issue.created.toDateString()}</td>
            <td>{props.issue.effort}</td>
            <td>{props.issue.due?props.issue.due.toDateString():""}</td>
            <td>{props.issue.title}</td>
        </tr>
    );
}

【问题讨论】:

  • 功能版也放在这里
  • sampleIssueissues 是什么?它们从未在您的功能组件中定义。

标签: javascript reactjs react-functional-component


【解决方案1】:

查看这个工作沙盒: https://codesandbox.io/s/confident-taussig-tg3my?file=/src/App.js

需要注意的一点是,您不应该将键作为线性序列 [或索引] 的基础,因为这会扰乱 React 的跟踪。相反,将 map 中的索引值传递给 IssueRow 组件以跟踪订单,并为每个问题提供自己的序列化 id。

 const issueRows = issues.map((issue, index) => (
    <IssueRow key={issue.id} issue={issue} index={index} />
  ));

此外,根据先前状态更新状态的正确方法是传递函数而不是对象:

 setIssues(state => [...state, issue]);

您实际上是在迭代以前的状态,并添加新问题。

【讨论】:

    【解决方案2】:

    功能组件可能如下所示:

    function IssueTable() {
      const [issue1, setIssues] = React.useState([]);
      const createIssue = React.useCallback(
        function createIssue(issue) {
          //passing callback to setIssues
          //  so useCallback does not depend
          //  on issue1 and does not have stale
          //  closure for issue1
          setIssues((issues) => {
            issue.id = issues.length + 1;
            issue.created = new Date();
            const newIssueList = issues.slice();
            newIssueList.push(issue);
            return newIssueList;
          });
        },
        []
      );
      React.useEffect(() => {
        setTimeout(() => {
          createIssue(sampleIssue);
        }, 2000);
      }, [createIssue]);
      React.useEffect(() => {
        setTimeout(() => {
          setIssues(initialIssues);
        }, 500);
      }, []);
    
      const issueRows = issue1.map((issue) => (
        <IssueRow key={issue.id} issue={issue} />
      ));
    
      return (
        <table className="bordered-table">
          <thead>
            <tr>
              <th>ID</th>
              <th>Status</th>
              <th>Owner</th>
              <th>Created</th>
              <th>Effort</th>
              <th>Due Date</th>
              <th>Title</th>
            </tr>
          </thead>
          <tbody>{issueRows}</tbody>
        </table>
      );
    }
    

    【讨论】:

      【解决方案3】:

      这应该在另一个React.useEffect()中:

      setTimeout(() => {
        createIssue(sampleIssue);
      }, 2000);
      

      否则,它将循环运行每个渲染。作为一般经验法则,除了 useEffect 之外应该没有副作用。

      【讨论】:

      • 感谢您的回答!!我将 setTimeout 包装到一个函数中,然后在 useEffect 中调用它。它可以正常工作,但问题是它以某种方式无法访问它在数组 issue1 中拥有的先前数据,并且只呈现一行。您能否调查一下并告诉我如何使用陈旧数据获取当前值而不是它。我是异步 js 的新手,不知道如何处理它
      猜你喜欢
      • 2021-11-27
      • 1970-01-01
      • 1970-01-01
      • 2020-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-22
      • 2021-05-29
      相关资源
      最近更新 更多