Подключить функцию за пределами состояния в React

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

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

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

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

Это мой код:

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 ответа

Решение

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

Во-первых, вам нужно передать обработчик при рендеринге 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>
  )
}

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

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

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

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

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

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

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

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

  1. В соответствии Content, Hits а также Hit в методе рендера Admin или извлеките их как renderContent а также renderHits Методы полей класса администратора, таким образом, вы сможете обновить состояние внутри методов этих методов.

  2. Вероятно, тот, который вы должны рассмотреть больше: Pass 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}/>
Другие вопросы по тегам