× Giới thiệu Lịch khai giảng Tin tức Sản phẩm học viên

Lifting State Up trong Reactjs cho người mới học

04/11/2022 01:17

Thông thường, một số thành phần cần phản ánh cùng một dữ liệu thay đổi. Chúng tôi khuyên bạn nên nâng trạng thái được chia sẻ lên đến tổ tiên chung gần nhất của chúng. Hãy xem điều này hoạt động như thế nào trong thực tế.

Thông thường, một số thành phần cần phản ánh cùng một dữ liệu thay đổi. Chúng tôi khuyên bạn nên nâng trạng thái được chia sẻ lên đến tổ tiên chung gần nhất của chúng. Hãy xem điều này hoạt động như thế nào trong thực tế.

Trong phần này, chúng tôi sẽ tạo một công cụ tính nhiệt độ để tính toán liệu nước có sôi ở nhiệt độ nhất định hay không.

Chúng ta sẽ bắt đầu với một thành phần được gọi là BoilingVerdict. Nó chấp nhận celsiusnhiệt độ như một chỗ dựa và in xem nó có đủ để đun sôi nước hay không:

function BoilingVerdict(props) {
  if (props.celsius >= 100) {
    return <p>The water would boil.</p>;  }
  return <p>The water would not boil.</p>;}

Tiếp theo, chúng ta sẽ tạo một thành phần được gọi là Calculator. Nó hiển thị một <input>cho phép bạn nhập nhiệt độ và giữ nguyên giá trị của nó this.state.temperature.

Ngoài ra, nó hiển thị BoilingVerdictgiá trị đầu vào hiện tại.

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};  }

  handleChange(e) {
    this.setState({temperature: e.target.value});  }

  render() {
    const temperature = this.state.temperature;    return (
      <fieldset>
        <legend>Enter temperature in Celsius:</legend>
        <input          value={temperature}          onChange={this.handleChange} />        <BoilingVerdict          celsius={parseFloat(temperature)} />      </fieldset>
    );
  }
}

 

Thêm đầu vào thứ hai

Yêu cầu mới của chúng tôi là, ngoài đầu vào độ C, chúng tôi cung cấp đầu vào độ F và chúng được giữ đồng bộ.

Chúng ta có thể bắt đầu bằng cách trích xuất một TemperatureInputthành phần từ Calculator. Chúng tôi sẽ thêm một chỗ dựa mới scalecho nó có thể là "c"hoặc "f":

const scaleNames = {  c: 'Celsius',  f: 'Fahrenheit'};
class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};
  }

  handleChange(e) {
    this.setState({temperature: e.target.value});
  }

  render() {
    const temperature = this.state.temperature;
    const scale = this.props.scale;    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

Bây giờ chúng ta có thể thay đổi Calculatorđể hiển thị hai đầu vào nhiệt độ riêng biệt:

class Calculator extends React.Component {
  render() {
    return (
      <div>
        <TemperatureInput scale="c" />        <TemperatureInput scale="f" />      </div>
    );
  }
}

Hãy thử nó trên CodePen

Chúng tôi hiện có hai đầu vào, nhưng khi bạn nhập nhiệt độ vào một trong hai đầu vào, đầu vào kia không cập nhật. Điều này mâu thuẫn với yêu cầu của chúng tôi: chúng tôi muốn giữ chúng đồng bộ.

Chúng tôi cũng không thể hiển thị BoilingVerdicttừ Calculator. Không Calculatorbiết nhiệt độ hiện tại vì nó được ẩn bên trong TemperatureInput.

Viết các hàm chuyển đổi

Đầu tiên, chúng ta sẽ viết hai hàm để chuyển đổi từ độ C sang độ F và ngược lại:

function toCelsius(fahrenheit) {
  return (fahrenheit - 32) * 5 / 9;
}

function toFahrenheit(celsius) {
  return (celsius * 9 / 5) + 32;
}

Hai hàm này chuyển đổi số. Chúng ta sẽ viết một hàm khác nhận một chuỗi temperaturevà một hàm chuyển đổi làm đối số và trả về một chuỗi. Chúng tôi sẽ sử dụng nó để tính toán giá trị của một đầu vào dựa trên đầu vào kia.

Nó trả về một chuỗi trống trên một chuỗi không hợp lệ temperaturevà nó giữ đầu ra được làm tròn đến chữ số thập phân thứ ba:

function tryConvert(temperature, convert) {
  const input = parseFloat(temperature);
  if (Number.isNaN(input)) {
    return '';
  }
  const output = convert(input);
  const rounded = Math.round(output * 1000) / 1000;
  return rounded.toString();
}

Ví dụ, tryConvert('abc', toCelsius)trả về một chuỗi trống và tryConvert('10.22', toFahrenheit)trả về '50.396'.

Lifting State Up trong Reactjs 

Hiện tại, cả hai TemperatureInputthành phần đều giữ các giá trị của chúng ở trạng thái cục bộ một cách độc lập:

class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = {temperature: ''};  }

  handleChange(e) {
    this.setState({temperature: e.target.value});  }

  render() {
    const temperature = this.state.temperature;    // ...  

Tuy nhiên, chúng tôi muốn hai đầu vào này đồng bộ với nhau. Khi chúng tôi cập nhật đầu vào độ C, đầu vào độ F sẽ phản ánh nhiệt độ đã chuyển đổi và ngược lại.

Trong React, trạng thái chia sẻ được thực hiện bằng cách chuyển nó đến tổ tiên chung gần nhất của các thành phần cần nó. Đây được gọi là "nâng trạng thái lên". Chúng tôi sẽ xóa trạng thái cục bộ khỏi TemperatureInputvà chuyển nó vào Calculatorthay thế.

Nếu Calculatorsở hữu trạng thái được chia sẻ, nó sẽ trở thành "nguồn chân lý" cho nhiệt độ hiện tại ở cả hai đầu vào. Nó có thể hướng dẫn cả hai có các giá trị nhất quán với nhau. Vì đạo cụ của cả hai TemperatureInputthành phần đều đến từ cùng một Calculatorthành phần mẹ, nên hai đầu vào sẽ luôn đồng bộ.

Hãy xem cách này hoạt động từng bước.

Đầu tiên, chúng tôi sẽ thay thế this.state.temperaturebằng this.props.temperaturetrong TemperatureInputthành phần. Hiện tại, hãy giả this.props.temperaturesử đã tồn tại, mặc dù chúng ta sẽ cần phải chuyển nó từ Calculatortrong tương lai:

  render() {
    // Before: const temperature = this.state.temperature;
    const temperature = this.props.temperature;    // ...

Chúng tôi biết rằngprop ở chế độ chỉ đọc. Khi temperatureở trạng thái cục bộ, TemperatureInputbạn chỉ cần gọi this.setState()để thay đổi nó. Tuy nhiên, bây giờ cái temperatuređang đến từ cha mẹ như một chỗ dựa, TemperatureInputnó không có quyền kiểm soát nó.

Trong React, điều này thường được giải quyết bằng cách tạo một thành phần "được kiểm soát". Giống như DOM <input>chấp nhận cả a valuevà onChangeprop, do đó, tùy chỉnh có thể TemperatureInputchấp nhận cả hai temperaturevà onTemperatureChangeprops từ cha của nó Calculator.

Bây giờ, khi TemperatureInputmuốn cập nhật nhiệt độ của nó, nó sẽ gọi this.props.onTemperatureChange:

  handleChange(e) {
    // Before: this.setState({temperature: e.target.value});
    this.props.onTemperatureChange(e.target.value);    // ...

Ghi chú:

Không có ý nghĩa đặc biệt đối với một trong hai temperaturehoặc onTemperatureChangetên hỗ trợ trong các thành phần tùy chỉnh. Chúng ta có thể gọi chúng bằng bất cứ thứ gì khác, chẳng hạn như đặt tên cho chúng valuevà onChangeđó là một quy ước chung.

Phần onTemperatureChangehỗ trợ sẽ được cung cấp cùng với phần temperaturehỗ trợ bởi Calculatorthành phần mẹ. Nó sẽ xử lý thay đổi bằng cách sửa đổi trạng thái cục bộ của chính nó, do đó hiển thị lại cả hai đầu vào với các giá trị mới. Chúng tôi sẽ sớm xem xét việc Calculatortriển khai mới.

Trước khi đi sâu vào các thay đổi trong Calculator, hãy tóm tắt lại các thay đổi của chúng tôi đối với TemperatureInputthành phần. Chúng tôi đã xóa trạng thái cục bộ khỏi nó và thay vì đọc this.state.temperature, giờ chúng tôi đọc this.props.temperature. Thay vì gọi this.setState()khi chúng tôi muốn thực hiện thay đổi, bây giờ chúng tôi gọi this.props.onTemperatureChange(), sẽ được cung cấp bởi Calculator:

class TemperatureInput extends React.Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
  }

  handleChange(e) {
    this.props.onTemperatureChange(e.target.value);  }

  render() {
    const temperature = this.props.temperature;    const scale = this.props.scale;
    return (
      <fieldset>
        <legend>Enter temperature in {scaleNames[scale]}:</legend>
        <input value={temperature}
               onChange={this.handleChange} />
      </fieldset>
    );
  }
}

Bây giờ chúng ta hãy chuyển sang Calculatorthành phần.

Chúng tôi sẽ lưu trữ dữ liệu đầu vào hiện tại temperaturevà scaleở trạng thái cục bộ của nó. Đây là trạng thái mà chúng tôi “nâng lên” từ các đầu vào, và nó sẽ đóng vai trò là “nguồn chân lý” cho cả hai. Đây là đại diện tối thiểu của tất cả dữ liệu chúng ta cần biết để hiển thị cả hai đầu vào.

Ví dụ: nếu chúng ta nhập 37 vào đầu vào độ C, trạng thái của Calculatorthành phần sẽ là:

{
  temperature: '37',
  scale: 'c'
}

Nếu sau đó chúng tôi chỉnh sửa trường Fahrenheit thành 212, trạng thái của trường Calculatorsẽ là:

{
  temperature: '212',
  scale: 'f'
}

Chúng tôi có thể đã lưu trữ giá trị của cả hai đầu vào nhưng hóa ra là không cần thiết. Nó đủ để lưu trữ giá trị của đầu vào được thay đổi gần đây nhất và quy mô mà nó đại diện. Sau đó, chúng ta có thể suy ra giá trị của đầu vào khác dựa trên hiện tại temperaturevà scalemột mình.

Các đầu vào vẫn đồng bộ vì các giá trị của chúng được tính từ cùng một trạng thái:

class Calculator extends React.Component {
  constructor(props) {
    super(props);
    this.handleCelsiusChange = this.handleCelsiusChange.bind(this);
    this.handleFahrenheitChange = this.handleFahrenheitChange.bind(this);
    this.state = {temperature: '', scale: 'c'};  }

  handleCelsiusChange(temperature) {
    this.setState({scale: 'c', temperature});  }

  handleFahrenheitChange(temperature) {
    this.setState({scale: 'f', temperature});  }

  render() {
    const scale = this.state.scale;    const temperature = this.state.temperature;    const celsius = scale === 'f' ? tryConvert(temperature, toCelsius) : temperature;    const fahrenheit = scale === 'c' ? tryConvert(temperature, toFahrenheit) : temperature;
    return (
      <div>
        <TemperatureInput
          scale="c"
          temperature={celsius}          onTemperatureChange={this.handleCelsiusChange} />        <TemperatureInput
          scale="f"
          temperature={fahrenheit}          onTemperatureChange={this.handleFahrenheitChange} />        <BoilingVerdict
          celsius={parseFloat(celsius)} />      </div>
    );
  }
}

Hãy thử nó trên CodePen

Bây giờ, bất kể bạn chỉnh sửa đầu vào nào và this.state.temperaturecập nhật. Một trong các đầu vào nhận giá trị như hiện tại, vì vậy bất kỳ đầu vào nào của người dùng đều được giữ nguyên và giá trị đầu vào khác luôn được tính toán lại dựa trên giá trị đó.this.state.scaleCalculator

Hãy tóm tắt lại những gì sẽ xảy ra khi bạn chỉnh sửa thông tin đầu vào:

  • React gọi hàm được chỉ định như onChangetrên DOM <input>. Trong trường hợp của chúng tôi, đây là handleChangephương thức trong TemperatureInputthành phần.
  • Phương handleChangethức trong TemperatureInputthành phần gọi this.props.onTemperatureChange()với giá trị mong muốn mới. Các đạo cụ của nó, bao gồm onTemperatureChange, được cung cấp bởi thành phần chính của nó Calculator,.
  • Khi nó hiển thị trước đó, giá trị Calculatorđã chỉ định onTemperatureChangecủa độ C TemperatureInputlà phương thức Calculatorcủa handleCelsiusChangevà onTemperatureChangecủa độ F TemperatureInputlà phương thức Calculatorcủa handleFahrenheitChange. Vì vậy, một trong hai Calculatorphương thức này được gọi tùy thuộc vào đầu vào mà chúng tôi đã chỉnh sửa.
  • Bên trong các phương thức này, Calculatorthành phần yêu cầu React tự kết xuất lại bằng cách gọi this.setState()giá trị đầu vào mới và tỷ lệ đầu vào hiện tại mà chúng ta vừa chỉnh sửa.
  • React gọi phương thức Calculatorcủa thành phần renderđể tìm hiểu giao diện người dùng sẽ như thế nào. Giá trị của cả hai đầu vào được tính toán lại dựa trên nhiệt độ hiện tại và thang đo hoạt động. Việc chuyển đổi nhiệt độ được thực hiện ở đây.
  • React gọi các renderphương thức của các TemperatureInputthành phần riêng lẻ với các đạo cụ mới của chúng được chỉ định bởi Calculator. Nó tìm hiểu giao diện người dùng của họ trông như thế nào.
  • React gọi renderphương thức của BoilingVerdictcomponent, lấy nhiệt độ tính bằng độ C làm đạo cụ của nó.
  • React DOM cập nhật DOM với kết quả sôi động và để khớp với các giá trị đầu vào mong muốn. Đầu vào mà chúng tôi vừa chỉnh sửa nhận được giá trị hiện tại của nó và đầu vào khác được cập nhật thành nhiệt độ sau khi chuyển đổi.

Mọi bản cập nhật đều trải qua các bước giống nhau để các đầu vào luôn đồng bộ.