【问题标题】:(New) React Context from a Nested Component not working(新)来自嵌套组件的反应上下文不起作用
【发布时间】:2019-10-04 14:12:26
【问题描述】:

我在使用“新”React 上下文 (https://reactjs.org/docs/context.html) 时遇到了严重的问题,无法像文档中想要/期望的那样工作。我正在使用 React v.16.8.6(升级可能需要很长时间,这是一个很大的应用程序)。我知道新旧东西之间有一些混合,但请不要卡在那个上面..

我这样做是为了尽可能灵活,但它不起作用。

问题是,对于contextAddToCart(..),它只执行空函数,而不是我在文档this.addToCart 中定义的函数。我在其他地方也有消费者。似乎它可能以错误的顺序执行此操作。或者每次组件导入 MinicartContext 时,它都会重置为空 fn.. 我不知道如何解决这个问题..

我将发布我认为最能解释它的相关代码:

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

【问题讨论】:

  • 我认为这很令人困惑,因为你有几个调用相同的函数,向上和向下传递值,你尝试过减速器模式吗?还是只是在上下文中创建函数并根据需要使用它们?
  • @OZZIE 你确定 AddToCartButton 组件安装在反应树的 MinicartContainer 组件下吗?我在代码中看不到它。
  • 很难从您发布的代码中看出问题所在。您可能没有正确连接到您的上下文。如果我不得不猜测您可能只是函数本身存在问题。这是一个与您所拥有的接近的工作示例。也许它会帮助codesandbox.io/s/infallible-gates-qox4c
  • 我在回答中遗漏的另一件事:在您的Header.jsx 文件中,使用MinicartContainerMinicart,删除MinicartContext 属性。

标签: javascript reactjs react-context


【解决方案1】:

在我看来,您还没有完全理解上下文 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>
} 

那么当使用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);
}

您也可以将 Wrapper Provider 创建为类组件,并将完整的状态作为值传递,但这是不必要的复杂性。

我建议您查看this guide 的上下文信息,同时避免在同一范围内使用相同的名称...您的 AddToCartButton.jsx 文件非常混乱:P

【讨论】:

  • 在消费者中它必须被命名为“价值”还是你可以在那里放任何东西?还是必须与提供者中的名称相同?
【解决方案2】:

我遇到的问题是我在多个地方都使用了&lt;MinicartContainer&gt;,但所有地方都应该是一回事。更改它以包装所有元素使其他元素在上下文更新时重置其状态。

所以我找到的唯一解决方案是将所有static(包括状态)放入MinicartContainer,并跟踪所有实例,然后在所有(需要的)实例上使用forceUpdate()。 (因为我从不做this.setState,否则什么都不会更新)

我虽然新的 React 上下文将完全替代 Redux 之类的东西,但就目前而言,它是一个非常模糊的规范,可以(有时)以非标准方式替代 Redux。

如果可以,只需将所有子 Consumers 包装为 单个 Provider 组件而没有任何副作用,那么您可以使其成为更干净的实现。也就是说,我认为我所做的一切都不是坏事,但不是人们期望的干净实现应该是什么样子。文档中也根本没有提到这种方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-10-27
    • 2022-08-13
    • 1970-01-01
    • 1970-01-01
    • 2021-07-04
    • 2016-06-09
    • 2017-07-29
    • 2020-12-17
    相关资源
    最近更新 更多