Подключить функцию за пределами состояния в React - PullRequest
0 голосов
/ 08 января 2019

У меня есть приложение со множеством элементов, а в административной области пользователь-администратор может удалять или изменять элементы (например, в бэк-офисе обычного сайта электронной коммерции). В логике, чтобы изменить элемент, пользователь нажимает кнопку «Изменить элемент», и модальное открытие.

Моя проблема в том, что я не могу открыть модальное при нажатии кнопки, потому что мой браузер показывает мне сообщение об ошибке, когда я нажимаю кнопку "изменить элемент": "Ошибка ввода: Не удается прочитать свойство 'setState' of undefined"

Для этой задачи я использую реагирующее модальное реагирование.

Я застрял на несколько дней, я был бы очень рад, если бы вы могли мне помочь. Заранее спасибо.

Это мой код:

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);

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Я не знаю, может ли этот ответ полностью решить вашу проблему, но, по крайней мере, он позволит вам открыть модал.

Во-первых, вам нужно передать обработчик при рендеринге компонента 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 мы должны усовершенствовать компонент Hit с помощью Javascript clojure, прежде чем передавать его вниз:

const Content = ({ onEdit }) => {

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

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

Можете ли вы попытаться сообщить, открывается ли модальное окно?

0 голосов
/ 08 января 2019

Состояние компонента заключено в компоненте, который его определяет. В вашем случае вы пытаетесь обновить состояние компонента от Admin до Hit. Из React.js Документация о состоянии и жизни

Состояние похоже на реквизит, но оно является частным и полностью контролируется компонент.

Но причина этой ошибки не в том, что Hit не может обновить состояние Admin, событие не пытается. Что происходит, когда вы нажимаете кнопку Modify Item, Hit пытается обновить свое собственное состояние, но это не будет работать по двум причинам:

  1. Прежде всего из той же документации:

Мы упоминали ранее, что компоненты, определенные как классы, имеют некоторые дополнительные функции. Локальное состояние именно таково: функция доступна только для классов.

  1. Даже если вы преобразуете компонент в компонент класса, состояние должно быть определено либо в конструкторе, либо как поле класса. Но это все равно не поможет, потому что, хотя вы сможете обновить состояние, оно будет Hit.

Итак, на вашем месте вы бы выбрали одно из следующих решений (в зависимости от ситуации).

  1. Inline Content, Hits и Hit в методе рендеринга Admin или извлеките их как renderContent и renderHits Методы поля класса 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}/>
...