在没有 Redux 的情况下使用 React 切换语言

Toggle language using React without Redux

我正在尝试在 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,所以代码很乱。抱歉。

谢谢。

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

  1. State updates are asynchronous。这意味着 this.state 不会 this.setState 调用后立即具有更新状态。

  2. 因为状态更新是异步的,如果你根据现有状态设置状态(你在几个地方,包括switchLanguage),你必须 使用你向其传递回调的 setState 版本,而不是你向其传递对象的版本;在回调中,使用回调接收的最新状态对象作为参数。

  3. 当您执行 this.setState({/*...*/desc: this.convertCode(/*...*/)}) 时,您在 调用 setState 之前调用 convertCode ,并传递其 return 值转换为 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,所以需要使用回调形式。相反,合并这两个调用并将 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

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

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