组件函数运行多次反应

时间:2018-03-07 09:52:58

标签: javascript reactjs react-router

我正在基于来自反应路由器4的match.params显示页面。出于某种原因,一些函数运行3次,这使我前2次未定义,第三次是我想要的结果。 / p>

所以我有这个Show组件,它包含来自主组件的道具“projects”。在这个show组件中,我在数组上执行find函数,其中包含props项目。这必须将project.id等级的1项返回match.params.id。这很好,如果我通过单击按钮(通过应用程序)访问页面,我会得到包含正确项目和属性的页面(例如project.name)。但是如果我在浏览器中使用URL进行重新加载或访问页面,则会收到错误消息,指出属性名称未定义。如果我查看控制台,我会看到我的console.logs被激活了3次。就像我说的那样,前两个是未定义的,第三个是给我想要的结果。我猜前两个导致了我得到的错误。

我的显示组件

import React from 'react'
import { Button } from 'reactstrap'

const Show = ({match, projects}) => {

    let project = projects.find((project) => {
        //Return project with the id equel to the match.params.id
        return project.id == match.params.id;
    });

    //Both these console.logs runs 3 times when visiting a link by url
    console.log(project); //this one gives me 2 times undefined, third time is the right item
    console.log(match.params.id)

    return(

        <div className="container p-40">

            <div className="projects-header">

                {/* If I echo project.name it will gives me 'cannot read property 'name' of undefined'  */}
                <h2>Project '{match.params.id}' {/* {project.name} */}</h2>

                <Button className="btn btn-primary" onClick={() => console.log('save')}>Save</Button>

            </div>

            <div className="project-edit">



            </div>

        </div>

    );

};

export default Show;

父组件中的路由

<Route exact path="/projects/show/:id" render={(props) => <Show {...props} projects={this.state.projects} />} />

有人为这个问题提供了很好的解决方案吗?我发现这是一个问题,我的用户无法通过网址路由。我不知道为什么我的show组件会通过url / reload激发3次。

编辑(添加父组件):

//Import react
import React, { Component } from 'react';

//Import custom components
import Sidebar from './components/js/Sidebar'
import Dashboard from './components/js/Dashboard'
import Projects from './components/js/Projects'
import Show from './components/js/projects/Show'

//Import styles
import './App.css';


//3rd party deps
import { BrowserRouter as Router, Route } from "react-router-dom";
import axios from 'axios'


class App extends Component {

  constructor() {
    super();

    this.state = {
      //Times / Time tracking
      times: [],
      timer: false,
      currentTimer: 0,

      //Current task
      currentTask: {
        id: 3,
        title: '',
        project_id: {
          id: '',
          name: '',
          color: ''
        },
        date: '',
        time_total: ''
      },

      //Projects
      projects: []

    }

    this.addTask = this.addTask.bind(this);
    this.startTimer = this.startTimer.bind(this);
    this.stopTimer = this.stopTimer.bind(this);
    this.addProject = this.addProject.bind(this);

  }

  addTask = (task) => {

    let newArray = this.state.times.slice();

    newArray.push(task);

    this.setState({times: newArray, currentTimer: 0, timer: false});

    clearInterval(this.timerID);

  }

  addProject = (project) => {

    let newArray = this.state.projects.slice();

    newArray.push(project);

    this.setState({ projects: newArray });

  }


  startTimer() {

    let sec = this.state.currentTimer;

    const start = Date.now();

    this.setState({ timer: true });

    this.timerID = setInterval(() => {


      let time = new Date() - (start - sec * 1000);

      this.setState({ currentTimer: Math.round(time / 1000)});


    }, 1000);

  }

  stopTimer() {

    this.setState({ timer: false });
    console.log('stopped');

    clearInterval(this.timerID);
    //Clear interval here

  }

  componentDidMount() {

    // Make a request for a user with a given ID
    axios.get('/Sample.json')
      .then((response) => {

        this.setState({times: response.data});

    });

    axios.get('/Projects.json')
      .then((response) => {

        this.setState({projects: response.data});

    });


  }

  render() {
    return (

      <Router>

        <div className="page-wrapper">

          <Sidebar />

          <Route exact path="/" render={() => <Dashboard times={this.state.times} timer={this.state.timer} startTimer={this.startTimer} stopTimer={this.stopTimer} currentTimer={this.state.currentTimer} addTask={this.addTask} />} />
          <Route exact path="/projects" render={() => <Projects projects={this.state.projects} addProject={this.addProject} />} />
          <Route exact path="/projects/show/:id" render={(props) => <Show {...props} projects={this.state.projects} />} />

        </div>

      </Router>


    );
  }
}

export default App;

2 个答案:

答案 0 :(得分:1)

看起来当您的组件首次安装时,您将无法获得完全呈现应用所需的数据。

您正在进行API调用,该调用设置调用重新呈现的状态。然后,您将状态type传递给show组件。因此,当您的show组件首次尝试渲染时,它无法访问项目数据,但是当API调用完成并且最终渲染发生时,您将获得项目数据。

在一些条件检查中包装你的find函数,看看是否设置了项目

简单的事情:

projects

答案 1 :(得分:1)

您正在couple of async requests componentDidMountApp.js,因此当您重新加载时,这两个请求都会被触发,并且可以在不同时间返回响应,因此{{1触发调用导致渲染被称为3 two setState

times(1 at initial render and 2 after setStates),项目是一个空数组,因此您在initial render之后的第一次获得undefined

可能会发生find在它之后加载,导致setState调用,此时Sample.json是一个空数组,因此projects在子项中未定义

现在加载project时,Projects.json数组包含数组,项目将被定义

因此您需要为projects prop

添加条件检查
project

如果您想从项目中使用多个属性,可以将它们解构为

return(

        <div className="container p-40">

            <div className="projects-header">

                {/* If I echo project.name it will gives me 'cannot read property 'name' of undefined'  */}
                <h2>Project '{match.params.id}' {project && project.name} */}</h2>

                <Button className="btn btn-primary" onClick={() => console.log('save')}>Save</Button>

            </div>

            <div className="project-edit">



            </div>

        </div>

    );

P.S。请注意,这仅适用于一个级别的嵌套