反应-如何正确呈现诺言数组的进度?

时间:2018-11-09 08:39:59

标签: javascript reactjs

想象一下,我得到了一个诺言数组(总共12个诺言),我想呈现已在页面上解析的诺言进度:1 / 12、2 / 12、3 / 12之类的东西。因此,我从这里的答案中得到了解决方法:link

我成功地计算了progressNum或百分比,并且能够console.log它们。

问题是当我尝试使用setState设置progressNum时,当所有承诺都解决时,它仅显示12/12。或先渲染一些随机数字,例如4/12,然后再渲染12/12,但我想渲染诸如从1 / 12、2 / 12、3 / 13 ... 12/12开始的数字。

Im able to console.log the progress correctly

not render correctly

我知道setState是异步的,所以我尝试使用react ref来操纵元素。但是也没有运气。

到目前为止,我的代码:

class App extends Component {
  state = {
   progress: 0,
  };

  handleResize = async () => {
   ...

   // imgFiles is an array 12 File object

   this.allProgress(imgFiles.map(this.resizeImg));

   ...
  };


   allProgress(promArray) {
    let progress = 0;
    promArray.forEach((p) => {
      p.then(()=> {    
        progress += 1;
        console.log(`${progress}/12`);
        this.setState({ progress });
      });
    });
    return Promise.all(promArray);
  }

 // I use Jimp package to resize the img, and then return promise

   resizeImg = imgFile => new Promise((resolve, reject) => {
   const reader = new FileReader();
   reader.onloadend = () => {
     Jimp.read(reader.result)
         .then(async (jimpImg) => {
         const normalImg = await this.resizeMain(jimpImg, true, 
         imgFile.name);
         const largeImg = await this.resizeMain(jimpImg, false, 
         imgFile.name);
        resolve([normalImg, largeImg]);
      })
      .catch(reject);
     };
     reader.readAsArrayBuffer(imgFile);
   });

 render() {
  return (
    <div>
     <p>{this.state.progress}</p>
     <button onClick={this.handleResize} >Resize</button>
    </div> )
 }

我也尝试参考

class App extends Component {
 state = {
  progress: 0,
 };

 indicator = React.createRef();

 changeProgress = (num) => {
    this.indicator.current.innerText = `${num}/12`;
  };

 ...

 allProgress(promArray) {
  let progress = 0;
  promArray.forEach((p) => {
   p.then(()=> {    
    progress += 1;
    console.log(`${progress}/12`);

    // the only logic that I changed:        

    this.changeProgress(progress);
   });
  });
   return Promise.all(promArray);
 } 

 ...

 render() {
  return (
   <div>
    <p ref={this.indicator} />
    <button onClick={this.handleResize} >Resize</button>
   </div> )
 }
}

2 个答案:

答案 0 :(得分:1)

要更新进度时,可以使用setState的回调版本,以确保不尝试使用旧值进行更新。

示例

class App extends React.Component {
  state = {
    done: 0,
    total: 12,
    data: []
  };

  componentDidMount() {
    Promise.all(
      Array.from({ length: this.state.total }, () => {
        return new Promise(resolve => {
          setTimeout(() => {
            this.setState(
              prevState => ({ done: prevState.done + 1 }),
              () => resolve(Math.random())
            );
          }, Math.random() * 3000);
        });
      })
    ).then(data => {
      this.setState({ data });
    });
  }

  render() {
    const { done, total, data } = this.state;
    return (
      <div>
        <div>
          {done} / {total} done
        </div>
        <div>{data.join(", ")}</div>
      </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>

答案 1 :(得分:1)

问题是您使用的Promise.all仅在所有的诺言都得到解决时才被调用。因此,您所有的then方法将一起被调用。因此,回调中的多个setState调用将被分批处理,这就是为什么看不到全部计数的原因。不要使用Promise.all来解决问题,而要让诺言按照其自然顺序解决。

class App extends React.Component{
  constructor(props){
    super(props)
    this.state = {progress: 0, total: 12}
  }

  allProgress(promArray) {
    let progress = 0;
    //dont use Promise.all
    promArray.forEach((p) => {
      p.then(()=> {    
        progress += 1;
        console.log(`${progress}/12`);
        this.setState({ progress });
      });
    });
  }

  componentDidMount(){
    const p = []
    for(let i =0;i<12;i++){
      p.push(new Promise((resolve, reject)=>{setTimeout(resolve, Math.random() * 5000)}))
    }
    this.allProgress(p)
  }
  render(){
    return (
      <div>{this.state.progress} / {this.state.total}</div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById("app"))
<div id="app"></div>
<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>