Как правильно вызывать API внутри модала, когда он виден в React?

#reactjs #react-native

#reactjs #react-native

Вопрос:

Предположим, что у меня есть модал внутри компонента, и я хочу показывать модал при нажатии некоторых кнопок:

 render(){
  ...
  <Modal 
   is={this.state.productId} 
   visilble={this.state.visible} 
  />
}
 

Внутри модального компонента я хочу вызвать API для получения сведений о продукте на основе выбранного идентификатора следующим образом:

  componentWillReceiveProps(nextProps) {
    if(nextProps.visible amp;amp; !this.props.visible) {
      fetch(...).then(res => {
        this.setState({
          product: res.data
        })
      }).catch(err => {
        ...
      })
    }
  }
 

В документах React говорится, что componentWillReceiveProps, componentWillUpdate устарели, и вам следует избегать их в новом коде.Поэтому я пытаюсь использовать статический getDerivedStateFromProps()

 static getDerivedStateFromProps()(nextProps) {
  if(nextProps.visible amp;amp; ...) {
    fetch(...).then(res => {
      return {
       product: res.data
      }
    }).catch(err => {
      ...
    })
   } else return null (or just return null here without else)
}
 

Приведенный выше код не работает, поскольку выборка является асинхронной, поэтому она всегда возвращает null или ничего не возвращает, вы не можете использовать await здесь, чтобы дождаться разрешения API, и я слышал, что getDerivedStateFromProps не следует использовать для выборки данных.

Итак, каков наилучший способ решения проблемы?

Ответ №1:

Я думаю, лучше решить, показывать ли модальный компонент или нет в родительском компоненте, поскольку модальный компонент должен быть функциональным компонентом для отображения только модального связанного представления. Таким образом, каждый раз, когда модальный компонент не будет отображаться и отображаться только тогда, когда видимый флаг имеет значение true.

 { this.state.visible amp;amp;
  <Modal />
}
 

В родительском компоненте вы можете извлекать данные в componentDidMount, если сразу после первоначального рендеринга требуются данные, или componentDidUpdate, если после каждого обновления требуется выборка данных для модального. После получения данных установите состояние visible в true.

Счастливого кодирования!!!

Комментарии:

1. Я обновил вопрос, модальный должен вызывать API на основе идентификатора продукта, переданного из родительского компонента

2. Просто создайте компонент-оболочку для модального, скажем, ModalContainer, вызовите api из ModalContainer и, когда данные будут извлечены (visible: true), затем отобразите модальный компонент. Разделение проблем.

Ответ №2:

Вы можете смонтировать Modal основанный на this.state.visible и начать выборку, когда Modal он смонтирован на componentDidMount или когда реквизиты меняются через componentDidUpdate

 // delay
const delay = () => new Promise(res => setTimeout(res, 1000));

// fake products
const products = [
  { id: 1, text: "product 1" },
  { id: 2, text: "product 2" },
  { id: 3, text: "product 3" }
];

// fake ajax call
const API = async productId => {
  await delay();
  return products.find(p => p.id === productId);
};

class Parent extends Component {
  state = { productId: 1, visible: false };

  toggleShow = () => {
    this.setState(prevState => ({ visible: !prevState.visible }));
  };

  setProductId = productId => this.setState({ productId, visible: true });

  render() {
    return (
      <div className="App">
        <button onClick={this.toggleShow}>show or hide</button>
        <button onClick={() => this.setProductId(1)}>fetch product 1</button>
        <button onClick={() => this.setProductId(2)}>fetch product 2</button>
        <button onClick={() => this.setProductId(3)}>fetch product 3</button>
        <button onClick={() => this.setProductId(4)}>unknown product id</button>
        {this.state.visible amp;amp; <Modal is={this.state.productId} />}
      </div>
    );
  }
}

class Modal extends Component {
  state = { product: null, fetching: false };

  componentDidMount = () => {
    this.fetchProduct();
  };

  componentDidUpdate = prevProps => {
    if (prevProps.is !== this.props.is) {
      this.fetchProduct();
    }
  };

  fetchProduct = async () => {
    this.setState({ fetching: true });
    const product = await API(this.props.is);
    this.setState({ product, fetching: false });
  };

  render() {
    const { product, fetching } = this.state;

    if (fetching) return <h1>{`fetching product ${this.props.is}`}</h1>;

    return product ? (
      <div>
        <h1>{`product id: ${product.id}`}</h1>
        <h3>{`text: ${product.text}`}</h3>
      </div>
    ) : (
      <h1>Product not found</h1>
    );
  }
}
 

Песочница

Ответ №3:

На самом деле в другом подходе вы можете использовать <modal/> в другом PureComponent (например: componentContainer ) и просто вызвать его в своем основном представлении. и используйте только один объект внутри вашего основного состояния представления в качестве <componentContainer/> области данных и передайте его как свойство, подобное этому коду

   construcor(prop){
        super(props);
         this.state={
          productDetail:null<<change this in data fetch
          }
   }
<componentContainer data={this.state.productDetail} modalVisible={this.state.changeNumderModal}/>
 

и внутри вашего контейнера компонентов:

      <Modal
            animationType="fade"
            transparent={true}
            visible={this.props.modalVisible}//<<<<<<<<<
            onRequestClose={() => {

            }}>
                    //contents:
                     this.props.data.map(()=>//-----)
            </View>
        </Modal>
 

в этом методе у вас есть только один модальный и одна область в качестве его данных, вы можете вызывать выборки и другие функции в вашем основном компоненте как другие… и если это так необходимо, вы даже можете передать функцию как свойство в модальный контейнер, чтобы :

    someFunction(someval){
      //do some thing  
      }

    <componentContainer data={this.state.productDetail} modalVisible={this.state.changeNumderModal} someFunction={(val)=>this.someFunction(val)}/>
 

и вызовите его внутри :

           this.props.someFunction('what ever you want')
 

Ответ №4:

Итак, давайте предположим, что вы скрыли и показали модальный с помощью нажатия кнопки, теперь всякий раз, когда модель будет открываться внутри функции componentWillMount

 class Modal extends Component {

  this.state = {product:[], loader : false}

  componentWillMount(){
    fetch(...).then(res => {
     this.setState({
       product: res.data
     })
    }).catch(err => {
      ...
    })
  }

  render(){
    const {product, loader} = this.state;
    return (
      if (loader) return <ProductComponent data = {product}/> 
      else return <div> No data found </div>
    );
  }

}
 

Ответ №5:

Вы можете использовать следующий подход:

 componentDidUpdate(prevProps, prevState, snapshot) {
    if (this.props.visible amp;amp; !prevProps.visible) {
        // make the API call here
    }
}