【问题标题】:Connect function outside the state in React在 React 中连接状态外的函数
【发布时间】:2019-06-03 02:08:27
【问题描述】:

我有一个包含许多项目集合的应用程序,在管理区域,管理员用户可以删除或修改项目(如传统电子商务网站后台)。在逻辑上,为了修改一个项目,用户点击“修改项目”按钮,一个模式打开。

我的问题是我无法在点击按钮上打开模式,因为当我点击“修改项目”按钮时,我的浏览器会显示一条错误消息:“TypeError: Cannot read property 'setState' of undefined”

我使用 react-modal-responsive 来完成这个任务。

我被困了好几天,如果你能帮助我,我会非常高兴。预先感谢。

这是我的代码:

import React, { Component } from 'react';
import { database } from '../firebase/firebase';
import * as firebase from 'firebase';
import withAuthorization from './withAuthorization';
import _ from 'lodash';
import AuthUserContext from './AuthUserContext';
import FileUploader from "react-firebase-file-uploader";
import Image from 'react-image-resizer';
import{InstantSearch, SearchBox, Hits, Highlight, RefinementList} from "react-instantsearch/dom";
import Modal from 'react-responsive-modal';



function removeToCatalogue(hit) { 
  const item = hit.objectID;
    firebase.database().ref(`catalogue/${item}`).remove();
    console.log("Capsule correctement supprimée du catalogue")
}


const Hit = ({hit}) =>
    <div className="item">
       <img src={hit.avatarURL} width={150} height={150}></img>
        <h1 className="marque">{hit.marque}</h1>
        <h3 className="numero">{hit.numero}</h3>
        <h4 className="reference">{hit.reference}</h4>
        <h4 className="marquesuite">{hit.marquesuite}</h4>
        <p className="cote">{hit.cote}</p>

        <button className="btn btn-danger" onClick={() => removeToCatalogue(hit)}>Supprimer</button> 
        <button onClick={() => this.setState({open: true})}>Modify Item</button>
    </div>



const Content = () =>
  <div className="text-center">

    <Hits hitComponent={Hit}/>

  </div>


class Admin extends React.Component {
  constructor (props) {
    super (props);

    this.state = {
      marque: '',
      marquesuite: '',
      numero: '',
      reference: '',
      cote: '',
      avatar: "",
      isUploading: false,
      progress: 0,
      avatarURL: "",
      catalogue: {},
      open: false,

    };
    this.onInputChange = this.onInputChange.bind(this);
    this.onHandleSubmit = this.onHandleSubmit.bind(this);

  }


  onOpenModal = () => {
    this.setState({ open: true });
  };

  onCloseModal = () => {
    this.setState({ open: false });
  };


  onInputChange(e) {
    this.setState({
      [e.target.name]: e.target.value
    });
  }

  onHandleSubmit(e){
    e.preventDefault();
    const catalogue = {
      marque: this.state.marque,
      marquesuite: this.state.marquesuite,
      numero: this.state.numero,
      reference: this.state.reference,
      cote: this.state.cote,
      avatar: this.state.avatar,
      avatarURL: this.state.avatarURL,

    };
    database.push(catalogue);
    this.setState({
      marque: '',
      marquesuite: '',
      numero: '',
      reference: '',
      cote: '',
      avatar: "",
      isUploading: false,
      progress: 0,
      avatarURL: "",
    });
  }

  handleUploadStart = () => this.setState({ isUploading: true, progress: 0 });
  handleProgress = progress => this.setState({ progress });
  handleUploadError = error => {
    this.setState({ isUploading: false });
    console.error(error);
  };

  handleUploadSuccess = filename => {
    this.setState({ avatar: filename, progress: 100, isUploading: false });
    firebase
      .storage()
      .ref("images")
      .child(filename)
      .getDownloadURL()
      .then(url => this.setState({ avatarURL: url }));
  };


  render (){
    const { open } = this.state;
    return (
      <div className="container-fluid">
        <div className="container">
        <h1 class="text-center">Espace d'Administration</h1>
        <a href="https://super-capsule.000webhostapp.com/signaler-modification" class="btn btn-primary btn-lg active" role="button" aria-disabled="true">Signaler une modification</a>
        <form onSubmit={this.onHandleSubmit}>
          <div className="form-group">
          <label>Marque de la capsule:</label>
          <input
            value={this.state.marque}
            type="text"
            name='marque'
            placeholder="Marque"
            onChange={this.onInputChange}
            ref="marque"
            className="form-control" />
          </div>
          <div>
          <label>Numéro de la capsule:</label>
          <input
            value={this.state.numero}
            type="text"
            name='numero'
            placeholder="Numéro de la capsule"
            onChange={this.onInputChange}
            ref="numero"
            className="form-control"/>
          </div>
          <div className="form-group">
          <label>Référence de la capsule:</label>
          <input
            value={this.state.marquesuite}
            type="text"
            name='marquesuite'
            placeholder="Référence de la capsule"
            onChange={this.onInputChange}
            ref="marquesuite"
            className="form-control"/>
          </div>
          <div className="form-group">
          <label>Référence de la capsule (suite):</label>
          <input
            value={this.state.reference}
            type="text"
            name='reference'
            placeholder="Référence de la capsule (suite)"
            onChange={this.onInputChange}
            ref="reference"
            className="form-control"/>
          </div>
          <div className="form-group">
          <label>Cote de la capsule:</label>
          <input
            value={this.state.cote}
            type="text"
            name='cote'
            placeholder="Cote de la capsule"
            onChange={this.onInputChange}
            ref="cote"
            className="form-control"/>
          </div>

          <label>Visuel de la capsule:</label>
          {this.state.isUploading && <p>Progress: {this.state.progress}</p>}
          {this.state.avatarURL && <img src={this.state.avatarURL} />}
          <FileUploader
            accept="image/*"
            name="avatar"
            randomizeFilename
            storageRef={firebase.storage().ref("images")}
            onUploadStart={this.handleUploadStart}
            onUploadError={this.handleUploadError}
            onUploadSuccess={this.handleUploadSuccess}
            onProgress={this.handleProgress}
          />
          <button className="btn btn-info">Ajouter une capsule</button>
        </form>
      </div>

        <h1 className="text-center">Catalogue de capsule</h1>



        <InstantSearch
            apiKey="xxx"
            appId="xxx"
            indexName="xxx">


            <SearchBox translations={{placeholder:'Rechercher une capsule'}} width="500 px"/>

            <div>

                <Modal open={this.state.showModal} open={open} onClose={this.onCloseModal} center>
                  <h2>Simple centered modal</h2>
                </Modal>
            </div>

            <Content />    


          </InstantSearch>



      </div>
    )
  }
}



const authCondition = (authUser) => !!authUser;

export default withAuthorization(authCondition)(Admin);

【问题讨论】:

  • 那是因为你试图在类外访问 setState。

标签: javascript reactjs react-modal


【解决方案1】:

组件的状态包含在定义它的组件中。在您的情况下,您正在尝试通过 Hit 组件更新 Admin 的状态。来自React.js State and Life Documentation

State 类似于 props,但它是私有的,完全由 组件。

但是抛出这个错误的原因并不是因为Hit 不能更新Admin 的状态,它不会尝试更新。当您单击 Modify Item 按钮时会发生什么,Hit 正在尝试更新自己的状态,但这不起作用有两个原因:

  1. 首先来自同一文档:

我们之前提到定义为类的组件具有一些附加功能。本地状态就是这样:仅对类可用的功能。

  1. 即使您将组件转换为类组件,状态也应在构造函数或类字段中定义。但这无论如何都对您没有帮助,因为尽管您可以更新状态,但它将是 Hit 的状态。

所以,如果我是你,你会选择以下解决方案之一(视情况而定)。

  1. 在 Admin 的渲染方法中内联 ContentHitsHit 或将它们提取为 renderContentrenderHits Admin 的类字段方法,这样您就可以在此更新状态方法方法。

  2. 可能您应该考虑更多:将openModel 类字段函数作为onModifyItem 属性传递给Hits 组件,并将其用作onClick 属性Modify Item 按钮。

这是一个例子:

const Hit = ({onModifyItem, hit}) =>
    <div className="item">
       <img src={hit.avatarURL} width={150} height={150}></img>
        <h1 className="marque">{hit.marque}</h1>
        <h3 className="numero">{hit.numero}</h3>
        <h4 className="reference">{hit.reference}</h4>
        <h4 className="marquesuite">{hit.marquesuite}</h4>
        <p className="cote">{hit.cote}</p>

        <button className="btn btn-danger" onClick={() => removeToCatalogue(hit)}>Supprimer</button> 
        <button onClick={onModifyItem}>Modify Item</button>
    </div>


const Content = ({ onModifyItem }) => {
  const HitComponent = props => <Hit onModifyItem={onModifyItem} {...props}/>
  return (<div className="text-center">
    <Hits hitComponent={HitComponent}/>
  </div>);
}

<Content onModifyItem={this.openDialog}/>

【讨论】:

  • 你好@NValchev,谢谢你的帮助!!这一切现在对我来说似乎更清楚了。
【解决方案2】:

我不知道这个答案是否可以完全解决您的问题,但它至少可以让您打开模式。

首先,您需要在渲染Content 组件时传递一个处理程序,例如:

<Content onEdit={ this.onOpenModal } />

然后,我们应该在Hit 组件上有一个类似的处理程序,将它附加到按钮单击事件:

const Hit = ({ hit, onEdit }) =>
  <div className="item">
    <img src={hit.avatarURL} width={150} height={150}></img>
    <h1 className="marque">{hit.marque}</h1>
    <h3 className="numero">{hit.numero}</h3>
    <h4 className="reference">{hit.reference}</h4>
    <h4 className="marquesuite">{hit.marquesuite}</h4>
    <p className="cote">{hit.cote}</p>

    <button className="btn btn-danger" onClick={() => removeToCatalogue(hit)}>Supprimer</button> 
    <button onClick={ onEdit }>Modify Item</button>
  </div>

现在我们必须将此处理程序从 Content 组件传递给 Hit 组件。

我看到的问题是Hits 组件需要一个hitComponent 属性,它必须是要渲染的组件。这意味着,要传递 onEdit 处理程序,我们必须在传递之前使用 Javascript clojure 增强 Hit 组件:

const Content = ({ onEdit }) => {

  const EnhancedHit = props =>
     <Hit onEdit={ onEdit } { ...props } />

  return (
    <div className="text-center">  
      <Hits hitComponent={ EnhancedHit } />
    </div>
  )
}

如果它打开了模态,你可以尝试报告吗?

【讨论】:

  • 您好@0xc14m1z,非常感谢。这是完美的工作!谢谢!!
猜你喜欢
  • 2019-04-15
  • 2015-12-23
  • 2020-07-11
  • 2023-01-31
  • 1970-01-01
  • 2020-03-03
  • 2018-10-09
  • 1970-01-01
  • 2022-11-12
相关资源
最近更新 更多