基于类的组件延迟让道具反应

时间:2018-03-07 11:21:14

标签: javascript reactjs

我有这个基于类的组件,它具有来自父级的projects道具。在初始渲染时,prop为null。这将在父组件的lifecyclemethode中的一些调用之后设置。但是因为这需要一些时间,我的子组件上的this.props.projects一开始就为空。因此,如果我在我的子组件中设置一个状态(null),并且我尝试将状态设置为this.props.projects,我会得到null,因为我猜道道尚未加载。

现在我做一个秒的setTimeout来接收道具并设置状态。但我认为这不是最佳解决方案。我怎样才能更好地做到这一点?

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

class Show extends React.Component {

    constructor(props) {
        super(props);

        this.state = {
            project: null
        }

        this.run = this.run.bind(this);

    }

    componentWillMount() {

        this.run();

    }

    run() {

        setTimeout(() => {

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

            this.setState({project: project});

            console.log(this.state.project);


        }, 1000);

    }

    render() {

        return(

            <div className="container p-40">

                {this.state.project && this.state.project.name}

            </div>

        )
    }

}

export default Show;

如果我没有在this.run函数上设置超时,我会返回null。

//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/Show2'

//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

  }

  componentWillMount() {

    // 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 :(得分:0)

您可以在componentDidUpdate()中运行this.run()并检查道具是否已从null更改为设置

componentDidUpdate(prevProps) {
    if(this.props.projects !== prevProps.projects){
        this.run();
    }
}

答案 1 :(得分:0)

因此,似乎有两种情况需要合并:

  1. 您可以直接输入网址并访问"/projects/show/:id",这会导致<App/><Show/>组件一个接一个地加载projects状态<App/> 1}}是一个空数组,因为axios.get('/Projects.json')尚未完成。完成后,状态会更新,<App/>会向projects组件发送新的<Show/>道具。在这种情况下,您可以使用componentWillReceiveProps(nextProps)来比较新的和以前的道具并运行this.run()方法。
  2. 您可以先输入网址"/""/projects"并加载<App/>组件,过一段时间后,点击左右移动到/projects/show/:id。在这种情况下,projects中的<App/>状态可能已经更新,projects道具(此时不是空数组)已准备好传递给<Show/>组件。所以在这种情况下,使用<Show/>组件的第一次装载和渲染,projects道具具有包含值的实际数组。您可以使用componentWillMount()方法处理此问题并在此处运行this.run(),因为之后不会对道具进行任何更新,componentWillReceiveProps(nextProps)将不会触发,因为axios.get('/Projects.json')已经完成很久以前。
  3. 所以,我建议您处理这两种情况,因为它应该同时使用componentWillMount()componentWillReceiveProps(nextProps)方法并检查projects道具是否具有以下值:

    componentWillMount() {
            // check if projects props is not an empty array at initial load
            if (this.props.projects.length > 0) {
                this.run(this.props.projects);
            }
        }
    
    componentWillReceiveProps(nextProps) {
            // check if new and old projects props are different
            if (nextProps.projects.length > 0 && (nextProps.projects !== this.props.projects)) {
                this.run(nextProps.projects);
            }
        }
    
    // and finally update your run() method
    run(projects) {
        let project = projects.find((project) => {
            return project.id == this.props.match.params.id;
        });
        this.setState({project: project});
    }