首页 文章

在上传之前读取文件内容时,React呈现无限循环

提问于
浏览
0

当使用FileReader API选择上传文件时,我正在尝试在React / Redux应用程序中显示一些文件内容 . 我能够显示内容,但在FileReader的onload事件处理程序中调用setState会导致无限渲染 .

import _ from 'lodash';
import React, { Component } from 'react';
import { reduxForm, Field, formValueSelector } from 'redux-form';
import Button from 'material-ui/Button';
import * as actions from '../../actions';
import { connect } from 'react-redux';
import {
  Select,
  TextField,
} from 'redux-form-material-ui';

import { renderFileInput } from '../helpers/form_helpers';

class ImportLeads extends Component {
  state = {
    fields: []
  }

  handleFormSubmit({ leadsCSV }) {
    const { listid } = this.props;
    this.props.importLeads(leadsCSV, listid);
  }

  renderMapping() {
    const { CSVFile } = this.props;
    console.log(CSVFile);
    const temp = [];

    if(CSVFile) {
      const r = new FileReader();
      r.readAsText(CSVFile, "UTF-8");
      r.onload = (e) => {
        const content = e.target.result;
        const firstLine = content.split('\n', 1)[0];
        const fieldsArray = firstLine.split(',');
        console.log(fieldsArray);
        _.map(fieldsArray, field => {
          console.log(field);
          temp.push(<div>{field}</div>);
        });
        this.setState({ fields: temp });
      }
      r.onerror = function(e) {
        console.log("Error reading file");
      }
    }

    return (
      <div>
        {this.state.fields}
      </div>
    );
  }

  render() {
    const { handleSubmit } = this.props;

    return (
      <div>
        <form onSubmit={handleSubmit(this.handleFormSubmit.bind(this))}>
          <div>

            <Field
              name="leadsCSV"
              component={renderFileInput}
              type="file"
              text="Import CSV"
            />

            <div style={{ marginTop: '10px' }}>
              <Button type="submit" variant="raised">Upload</Button>
            </div>
          </div>
        </form>
        {this.renderMapping()}
      </div>
    );
  }
}

ImportLeads = reduxForm({
  form: 'importLeads',
})(ImportLeads);

const selector = formValueSelector('importLeads');
ImportLeads = connect(
  state => {
    const CSVFile = selector(state, 'leadsCSV');
    return {
      CSVFile
    }
  }
)(ImportLeads);

export default ImportLeads;

我使用redux表单来处理文件输入并通过使用connect将其作为props提供来获取文件的值 .

我读到在render方法中调用setState时通常会发生无限渲染 . 有没有更好的方法来解决这个问题,而不是让它无限渲染?谢谢 .

3 回答

  • 0

    React具有特殊的“生命周期”方法,对于这个用例非常有用 .

    如果定义 componentWillMount() 方法,则会在第一次渲染之前调用它 . 如果你有额外的资源,你需要加载你可以踢那里 .

    因为看起来你想重新运行那些计算,如果道具改变你也会想要使用 componentWillReceiveProps(nextProps) 来重新运行你的计算基于新的道具 . 请注意 this.props 尚未反映新道具,您需要从参数中获取这些道具 .

    最后,尽管你的情况不必要,你应该用 componentWillUnmount() 来熟化自己,这将允许你自己清理(例如删除事件监听器,取消计时器等),如果你不这样做,你很容易导致内存泄漏,甚至更糟 .

    您可以在文档中熟悉更多生命周期方法:https://reactjs.org/docs/react-component.html#the-component-lifecycle

    最后一个示例演示了如何使用这些方法来解决类似“解析”换行符分隔列表的问题 .

    class Table extends React.Component {
        constructor() {
          super();
          this.state = {
            rows: []
          };
        }
    
        componentWillMount() {
          this.parse(this.props.string);
        }
    
        componentWillReceiveProps(nextProps) {
          this.parse(nextProps.string);
          
        }
    
        parse(string) {
          // Dummy placeholder for asynchronous operation
          this.setState({
            rows: string.split("\n")
          });
        }
    
        render() {
          return (
            <ul>
              { this.state.rows.map((row) => <li>{row}</li>) }
            </ul>
          );
        }
    }
    
    class Sample extends React.Component {
      constructor() {
        super();
        this.state = { string: "hello world\ngoodbye world" };
      }
      
      render() {
        return (<div>
          <Table string={this.state.string} />
          <textarea onChange={(event) => this.setState({ string: event.target.value })} value={this.state.string} />
        </div>);
      }
    }
    
    ReactDOM.render(<Sample /> , 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>
    
  • 0

    renderMapping() 内部,调用 setState 导致重新渲染组件 .

    在组件 render 中,您有 {this.renderMapping()} 导致再次调用 renderMapping .

    这个过程重复n次导致无限渲染 .

  • 0

    如果 fieldsArray 已包含 field ,则应检入 renderMapping . 如果是这样,则不应将其推送到阵列 . (因为你将另一个div中的相同字段推入到数组中,它会不断地重新渲染)

    if (fieldsArray.includes(field)) {
      return;
    }
    

相关问题