首页 > 解决方案 > 为什么在将数据设置为上下文时出现超出最大更新深度的错误(React)

问题描述

在我的 React 项目中,我想使用上下文在组件中共享用户选择的货币。上下文功能工作正常,但是,我想解决一个小问题。它将默认货币设置为上下文。因此,当网络从一开始就启动时,将为上下文设置一个默认货币,这将来自端点提供的选择。我使用了 CurrencySelector.js 中所示的 if 语句,但我收到了“超过最大更新深度”错误。我为上下文和货币选择组件提供了我的代码。

import React, { Component, createContext } from "react";

const CurrencyContext = createContext();

class CurrencyContextProvider extends Component {
  state = { selectedCurrency: "uuu" };
  setCurrency = (c) => {
    this.setState({ selectedCurrency: c });
  };

  render() {
    return (
      <CurrencyContext.Provider
        value={{ ...this.state, setCurrency: this.setCurrency }}
      >
        {this.props.children}
      </CurrencyContext.Provider>
    );
  }
}

class CurrencySelector extends React.Component {
  constructor(props) {
    super(props);
    console.log("CurrencySelector constructor");
    this.state = { currencies: [] };
  }

  async componentDidMount() {
    let currencies = (await this.getCurrencies()).data
      .currencies;
    this.setState({ currencies: currencies });
  }

  async getCurrencies() {
    return await fetchAnyQuery(`query{ currencies }`);
  }

  render() {
    return (
      <CurrencyContext.Consumer>
        {(currencyContext) => {
          const {
            selectedCurrency,
            setCurrency,
          } = currencyContext;
          return (
            <select
              name="currency-selector"
              onChange={(event) => {
                setCurrency(event.target.value);
              }}
            >
              {this.state.currencies.map((currency, index) => {
                //When I wrote this if-statement I got the error
                if (index == 0) {
                  setCurrency(currency);
                }
                return (
                  <option value={currency}>{currency}</option>
                );
              })}
            </select>
          );
        }}
      </CurrencyContext.Consumer>
    );
  }
}

标签: javascriptreactjs

解决方案


//When I wrote this if-statement I got the error
if (index == 0) {
  setCurrency(currency);
}

是的——你会得到一个错误,因为你在 render 期间改变了状态,这是明确禁止的。(这并不特定于使用上下文。)

你有几个我能想到的选择:

  • 将货币加载移动到上下文提供程序,并让上下文提供程序在加载货币后设置默认货币
  • 如果尚未设置默认货币,则“假”设置为默认货币(但这会很快变得毛茸茸)

这是第一个选项的示例。

查看如何getCurrencies()被提升到componentDidMount()提供者的位置。

需要格外小心,因为可能还没有加载货币;看看我们如何只在那个时候渲染“Loading...”。

import React, { Component, createContext } from "react";

const CurrencyContext = createContext();

// Fake fetcher function that just takes a bit of time.
function fetchAnyQuery() {
  return new Promise((resolve) =>
    setTimeout(() => resolve({ data: { currencies: ["a", "b", "c"] } }), 1000)
  );
}

class CurrencyContextProvider extends Component {
  state = { selectedCurrency: "uuu", currencies: undefined };
  setCurrency = (c) => {
    this.setState({ selectedCurrency: c });
  };

  async componentDidMount() {
    const currencies = (await this.getCurrencies()).data.currencies;

    this.setState(({ selectedCurrency }) => {
      if (!currencies.includes(selectedCurrency)) {
        // If the selected currency is invalid, reset it to the first one
        selectedCurrency = currencies[0];
      }
      return { currencies, selectedCurrency };
    });
  }

  async getCurrencies() {
    return await fetchAnyQuery(`query{ currencies }`);
  }

  render() {
    return (
      <CurrencyContext.Provider
        value={{ ...this.state, setCurrency: this.setCurrency }}
      >
        {this.state.currencies ? this.props.children : <>Loading...</>}
      </CurrencyContext.Provider>
    );
  }
}

class CurrencySelector extends React.Component {
  render() {
    return (
      <CurrencyContext.Consumer>
        {(currencyContext) => {
          const { selectedCurrency, currencies, setCurrency } = currencyContext;
          return (
            <div>
              <select
                name="currency-selector"
                value={selectedCurrency}
                onChange={(event) => {
                  setCurrency(event.target.value);
                }}
              >
                {currencies.map((currency) => (
                  <option value={currency} key={currency}>
                    {currency}
                  </option>
                ))}
              </select>
              <br />
              Selected: {selectedCurrency}
            </div>
          );
        }}
      </CurrencyContext.Consumer>
    );
  }
}

export default function App() {
  return (
    <CurrencyContextProvider>
      <CurrencySelector />
    </CurrencyContextProvider>
  );
}

推荐阅读