(Новый) Реакция контекста из вложенного компонента не работает - PullRequest
0 голосов
/ 04 октября 2019

У меня серьезные проблемы с «новым» React Context (https://reactjs.org/docs/context.html), чтобы работать так, как я хочу / ожидаю от документации. Я использую React v.16.8.6 (обновление, вероятно, займет много времени, это большое приложение). Я знаю, что между старым и новым есть что-то вроде смеси, но, пожалуйста, не зацикливайтесь на этом ..

Я сделал это так, чтобы быть максимально гибким, но это не работает.

Проблема в , когда дело доходит до contextAddToCart(..), она выполняет только пустую функцию вместо той, которую я определил в состоянии как документация this.addToCart. У меня есть потребители и в других местах. Кажется, возможно, он выполняет это в неправильном порядке. Или каждый раз, когда Компонент импортирует MinicartContext, он сбрасывается в пустую строку. Я не знаю, как обойти это ..

Я просто выложу соответствующий код, который, я думаю, объяснит это лучше:

webpack.config.js :

const APP_DIR = path.resolve(__dirname, 'src/');
module.exports = function config(env, argv = {}) {
  return {
    resolve: {
      extensions: ['.js', '.jsx'],
      modules: [
        path.resolve(__dirname, 'src/'),
        'node_modules',
      ],
      alias: {
        contexts: path.resolve(__dirname, './src/contexts.js'),
      },

contexts.js

import React from 'react';

export const MinicartContext = React.createContext({
  addToCart: () => {},
  getState: () => {},
});

MinicartContainer.jsx

import React, { Component } from 'react';
import PropTypes from 'prop-types';

import {
  MinicartContext,
} from 'contexts';

export default class MinicartContainer extends Component {
  constructor(props) {
    super(props);

    this.addToCart = (product, qty) => {
      const { prices } = product;
      const { grandTotal, qtyTotal } = this.state;

      this.setState({
        grandTotal: grandTotal + prices.price,
        qtyTotal: qtyTotal + qty,
      });
    };

    this.state = {
      grandTotal: -1,
      qtyTotal: -1,
      currencyCode: '',
      addToCart: this.addToCart,
    };
  }    

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

    return (
      <MinicartContext.Provider value={this.state}>
        {children}
      </MinicartContext.Provider>
    );
  }

Header.jsx :

import React, { Component } from 'react';
import {
  MinicartContext,
} from 'contexts';

class Header extends Component {

  render() {
    return (
      <div>
        <MinicartContainer MinicartContext={MinicartContext}>
          <Minicart MinicartContext={MinicartContext} />
        </MinicartContainer MinicartContext={MinicartContext}>

        {/* stuff */}

        <MinicartContainer MinicartContext={MinicartContext}>
          <Minicart MinicartContext={MinicartContext} />
        </MinicartContainer MinicartContext={MinicartContext}>
      </div>
    )
  }
}
export default Header;

AddToCartButton.jsx

import {
  MinicartContext,
} from 'contexts';    

export default class AddToCartButton extends Component {

  addToCart(e, contextAddToCart) {
    e.preventDefault();
    const QTY = 1;
    const { product, active } = this.props; 

    // doing stuff ...   

    contextAddToCart(product, QTY);
  }

  render() {       
    return (
      <React.Fragment>
        <MinicartContext.Consumer>
          {({context, addToCart}) => (
            <div
              onClick={(e) => { this.addToCart(e, addToCart); }}    

Ответы [ 2 ]

0 голосов
/ 16 октября 2019

Проблема, с которой я столкнулся, заключалась в том, что я использовал <MinicartContainer> в нескольких местах, но все они должны действовать как одно и то же. Изменяя его так, чтобы он охватывал все элементы, другие элементы сбрасывали свое состояние при обновлении контекста.

Таким образом, единственное решение, которое я нашел, было сделать все static (включая состояние) внутри MinicartContainer и отслеживать все экземпляры, а затем использовать forceUpdate() на всех (необходимых) экземплярах. (Поскольку я никогда не делаю this.setState, в противном случае ничего не обновляется)

Я думаю, что новый контекст React будет чистой заменой для таких вещей, как Redux, но в сегодняшнем виде это более туманная спецификация, которая может заменить Redux(иногда) нестандартным способом.

Если вы можете просто обернуть все дочерние Consumer s компонентом single Provider без каких-либо побочных эффектов, тогда выможет сделать это более чистой реализацией. Тем не менее, я не думаю, что то, что я сделал, плохое в любом случае, но не то, что люди ожидают, что чистая реализация должна выглядеть. Также этот подход вообще не упоминается в документации.

0 голосов
/ 04 октября 2019

Мне кажется, что вы не совсем понимаете, как работают контекстные API-слова.

Вот моя HOC-реализация контекстов, может быть, это поможет вам лучше понять, как все работает.

export const MinicartContext = React.createContext({}) // Export the Context so we can use the Consumer in class and functional components (above). Don't use the Provider from here.

// Wrap the provider to add some custom values.
export const MinicartProvider = props => {
  const addToCart = () => {
    //Add a default version here
  };
  const getState = () => {
    //Add a default version here
  };

  // Get the custom values and override with instance ones.
  const value = {addToCart, getState, ...props.value}

  return <MinicartContext.Provider value={value}>
    {props.children} 
  </MinicartContext.Provider>
} 

Тогда при использовании провайдера:

const SomeComponent = props => {
  const addToCart = () => {
    //A custom version used only in this component, that need to override the default one
  };

  //Use the Wrapper, forget the MinicartContext.Provider
  return <MinicartProvider value={{addToCart}}>
    /* Stuff */
  </MinicartProvider>

}

А при использовании потребителя у вас есть три варианта:

Компоненты класса с одним контекстом

export default class AddToCartButton extends Component {
  static contextType = MinicartContext;

  render (){
    const {addToCart, getState} = this.context;
    return (/*Something*/)
  }
}

Компоненты класса с несколькими контекстами

export default class AddToCartButton extends Component {
  render (){
    return (
      <MinicartContext.Consumer>{value => {
        const {addToCart, getState} = value
        return (/*Something*/)
      }}</MinicartContext.Consumer>
    )
  }
}

Функциональные компоненты

const AddToCartButton = props => {
  const {addToCart, getState} = useContext(MinicartContext);
}

Вы можете создать провайдер-оболочку какКомпонент класса тоже, и передайте полное состояние как значение, но это ненужная сложность.

Я рекомендую вам взглянуть на это руководство о контекстах, а также избегать использования того же имени вта же самая область ... Ваш AddToCartButton.jsx файл был действительно запутанным: P

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...