【问题标题】:Toggle language using React without Redux在没有 Redux 的情况下使用 React 切换语言
【发布时间】:2019-02-25 17:25:12
【问题描述】:

我正在尝试在 React 中创建一个天气应用程序。我想使用onClick 切换应用语言。

这是我目前的代码。

import React, { Component } from "react";
import axios from "axios";
import "./App.css";

class App extends Component {

componentDidMount() {
    this.setState({
      isLoading: true
    });
    axios
      .get("path to weather api")
      .then(res => {
        console.log(res.data.data[0]);
        const { city_name, temp, weather } = res.data.data[0];
        this.setState({
          loc: city_name,
          temp: temp,
          code: weather.code,
          isLoading: false
        });
        this.setState({
          desc: this.convertCode(this.state.code)
        });
      });
  }

switchLanguage = () => {
    if (this.state.lang === "en") {
      this.setState({
        lang: "hi",
        desc: this.convertCode(this.state.code)
      });
    } else {
      this.setState({
        lang: "en",
        desc: this.convertCode(this.state.code)
      });
    }
  };

convertCode = givenCode => {
    if (this.state.lang === "en") {
      if (
        givenCode === 200 ||
        givenCode === 201 ||
        givenCode === 202 ||
        givenCode === 230 ||
        givenCode === 231 ||
        givenCode === 232 ||
        givenCode === 233 ||
        givenCode === "200" ||
        givenCode === "201" ||
        givenCode === "202" ||
        givenCode === "230" ||
        givenCode === "231" ||
        givenCode === "232" ||
        givenCode === "233"
      ) {
        return "Thunderstorms";
      } else if (
        givenCode === 300 ||
        givenCode === 301 ||
        givenCode === 302 ||
        givenCode === "300" ||
        givenCode === "301" ||
        givenCode === "302"
      ) {
        return "Drizzle";
      }
      ..............
      ..............
      IF CONDITION FOR THE OTHER LANGUAGE
  };

render() {
    if (!this.state.isLoading) {
      return (
        <div className="App">
          <div className="container">
            <div className="languageSwitcher">
              <i className="fa fa-language" onClick={this.switchLanguage} />
            </div>
            <div className="location">
              <i className="fa fa-location-arrow" /> {this.state.loc}
            </div>
            {this.state.lang === "en" && (
              <div className="temperature">It's {this.state.temp} degrees.</div>
            )}
            {this.state.lang === "hi" && (
              <div className="temperature">
                तापमान {this.state.temp} डिग्री है।
              </div>
            )}
            <div className="description">{this.state.desc}</div>
          </div>
        </div>
      );
    } else {
      return <div className="loading">Fetching weather data...</div>;
    }
  }
}

export default App;

除了divclassName="desc" 之外,一切都有效。 desc 总是落后一个阶段。我的意思是当state.langen 时,它会在hi 中显示文本,反之亦然。

我刚开始学习 React,所以代码很乱。对此感到抱歉。

谢谢。

【问题讨论】:

  • 你的意思是你需要点击onClick两次才能工作?
  • @KuchBhi 我认为他的意思是,如果语言是印地语,那么描述是英语,而当语言是英语时,描述是印地语。
  • 看看这是否有帮助:stackoverflow.com/questions/52433669/…
  • 是的@SivcanSingh 是对的。默认情况下,该应用程序在 en 中加载。因此,当我onClick 一次时,语言应该更改为hi,除了desc 之外的所有元素都会发生这种情况。同样在hi 的情况下,所有内容都变为hi,但desc 变为en。如果不清楚,我很抱歉。
  • @sequel 检查这个帖子:stackoverflow.com/questions/52433669/…

标签: javascript reactjs weather-api


【解决方案1】:

您有两个状态管理问题和一个执行流程问题,其中一个(或可能更多)导致您提到的行为(但无论如何都需要修复):

  1. State updates are asynchronous。这意味着this.statethis.setState 调用后不会立即获得更新的状态。

  2. 由于状态更新是异步的,如果您基于现有状态(在一些地方,包括 switchLanguage)设置状态,您必须使用setState 您将回调传递给,而不是您将对象传递给的版本;在回调中,使用回调接收到的最新状态对象作为参数。

  3. 当您执行this.setState({/*...*/desc: this.convertCode(/*...*/)}) 时,您调用convertCode 之前 调用setState,并将其返回值传递给setState 作为属性上的值对象你正在传递它。所以即使不是上面的问题#2,它仍然会有一个基本的控制流问题,convertCode 仍然会看到即将过期的this.state.lang

解决所有这些问题的最佳方法可能是更新convertCode 以选择接受lang 以使用(默认为this.state.lang):

convertCode = (givenCode, lang = this.state.lang) => {
    // ...use `lang`, not `this.state.lang`...

...然后解决设置状态和使用convertCode 的各种问题。第一个在componentDidMount

componentDidMount() {
    this.setState({
      isLoading: true
    });
    axios
      .get("path to weather api")
      .then(res => {
        console.log(res.data.data[0]);
        const { city_name, temp, weather } = res.data.data[0];
        this.setState({
          loc: city_name,
          temp: temp,
          code: weather.code,
          isLoading: false
        });
        this.setState({
          desc: this.convertCode(this.state.code) // <=== Error is here
        });
      });
  }

this.state.code 还没有更新,因为状态更新是异步的。另外,我们要使用this.state.lang,所以我们需要使用回调形式。相反,结合这两个调用并将语言传递给convertCode

        this.setState(prevState => ({
          loc: city_name,
          temp: temp,
          code: weather.code,
          isLoading: false,
          desc: this.convertCode(weather.code, prevState.lang),
        }));

switchLanguage 中,问题#2 和#3 都存在:

// INCORRECT:
//   A) Sets state based on state without callback
//   B) Calls `convertCode` before `setState`
switchLanguage = () => {
    if (this.state.lang === "en") {
      this.setState({
        lang: "hi",
        desc: this.convertCode(this.state.code)
      });
    } else {
      this.setState({
        lang: "en",
        desc: this.convertCode(this.state.code)
      });
    }
  };

我们可以通过使用回调表单并将要使用的语言传递给convertCode 来解决这两个问题:

// Uses callback when setting state based on state
switchLanguage = () => {
    this.setState(prevState => {
      const lang = prevState.lang === "en" ? "hi": "en";
      return {lang, desc: this.convertCode(prevState.code, lang)};
    });
  };

注意prevState 用于lang 检查和将code 传递给this.convertCode

【讨论】:

  • @sequel - 我认为在此过程中一定引入了一些错误,或者您的应用程序中的某些内容可能不像看起来那样。请使用 minimal reproducible example 来更新您的问题,以展示问题,最好是使用 Stack Snippets([&lt;&gt;] 工具栏按钮)的 runnable 问题。 Stack Snippets 支持 React,包括 JSX; here's how to do one.
  • 等等。我的convertCode 版本只有一个参数:givenCode。由于您告诉我将两个参数传递给convertCode,我将不得不更改convertCode 的函数定义。对吗?
  • @sequel - 是的,我已经在答案顶部附近展示了这样做。答案中的其他代码依赖于此。 (这不是解决问题的唯一方法,但它可能是最简单、最有效的方法。)
  • 谢谢。我会尝试编辑我的函数并让你知道。
  • 好的。最后,它正在工作。我的错,我没有注意到您在顶部提到的convertCode = (givenCode, lang = this.state.lang)。再次感谢。你太棒了。
【解决方案2】:

您在 convertCode 方法中使用了以前的 lang 状态,您可以将新的 lang 传递给它:

convertCode = (givenCode, lang) => {
    if (lang === "en") {
        ...

然后在您的switchLanguage 方法中:

this.setState({
        lang: "hi",
        desc: this.convertCode(this.state.code, "hi")
      });

编辑:首选 setState 的功能版本以避免在this.state.code 更新时出现不一致:

this.setState(prevState => ({
        lang: "hi",
        desc: this.convertCode(prevState.code, "hi")
      }));

【讨论】:

  • 我认为在这种情况下我们可以信任this.state.code 的值,因为它无法通过语言切换批量更新。
  • 我同意,但我不认为每个人都必须盲目地遵循这条规则,因为它会阻止批处理状态更新,并且在某些特定情况下可能会导致性能问题。
  • @Dyo 我试过你说的,但没用。我很感激你试图帮助我。谢谢。
  • @T.J.Crowder 好点,但这里的问题不在于“setState 是异步的”。这个问题是关于在设置新状态之前调用convertCode(如果我错了,你在回答中写了“之后”,请纠正我)
  • @TJCrowder 我会尽力传播这个词,我只是想调试(和重现)由于状态批处理导致的数据不一致是多么困难,所以更好地发挥安全!谢谢,我喜欢这场辩论。
猜你喜欢
  • 2017-02-01
  • 2023-01-11
  • 2016-09-27
  • 1970-01-01
  • 2020-11-05
  • 1970-01-01
  • 1970-01-01
  • 2021-06-03
  • 1970-01-01
相关资源
最近更新 更多