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 celsius
nhiệ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ị BoilingVerdict
giá 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 TemperatureInput
thành phần từ Calculator
. Chúng tôi sẽ thêm một chỗ dựa mới scale
cho 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ị BoilingVerdict
từ Calculator
. Không Calculator
biế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 temperature
và 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ệ temperature
và 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 TemperatureInput
thà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 TemperatureInput
và chuyển nó vào Calculator
thay thế.
Nếu Calculator
sở 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 TemperatureInput
thành phần đều đến từ cùng một Calculator
thà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.temperature
bằng this.props.temperature
trong TemperatureInput
thành phần. Hiện tại, hãy giả this.props.temperature
sử đã tồn tại, mặc dù chúng ta sẽ cần phải chuyển nó từ Calculator
trong 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ộ, TemperatureInput
bạ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, TemperatureInput
nó 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 value
và onChange
prop, do đó, tùy chỉnh có thể TemperatureInput
chấp nhận cả hai temperature
và onTemperatureChange
props từ cha của nó Calculator
.
Bây giờ, khi TemperatureInput
muố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
temperature
hoặconTemperatureChange
tê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úngvalue
vàonChange
đó là một quy ước chung.
Phần onTemperatureChange
hỗ trợ sẽ được cung cấp cùng với phần temperature
hỗ trợ bởi Calculator
thà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 Calculator
triể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 TemperatureInput
thà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 Calculator
thành phần.
Chúng tôi sẽ lưu trữ dữ liệu đầu vào hiện tại temperature
và 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 Calculator
thà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 Calculator
sẽ 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 temperature
và scale
mộ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.temperature
cậ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.scale
Calculator
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ư
onChange
trên DOM<input>
. Trong trường hợp của chúng tôi, đây làhandleChange
phương thức trongTemperatureInput
thành phần. - Phương
handleChange
thức trongTemperatureInput
thành phần gọithis.props.onTemperatureChange()
với giá trị mong muốn mới. Các đạo cụ của nó, bao gồmonTemperatureChange
, đượ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ỉ địnhonTemperatureChange
của độ CTemperatureInput
là phương thứcCalculator
củahandleCelsiusChange
vàonTemperatureChange
của độ FTemperatureInput
là phương thứcCalculator
củahandleFahrenheitChange
. Vì vậy, một trong haiCalculator
phươ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,
Calculator
thành phần yêu cầu React tự kết xuất lại bằng cách gọithis.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
Calculator
của thành phầnrender
để 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
render
phương thức của cácTemperatureInput
thành phần riêng lẻ với các đạo cụ mới của chúng được chỉ định bởiCalculator
. Nó tìm hiểu giao diện người dùng của họ trông như thế nào. - React gọi
render
phương thức củaBoilingVerdict
component, 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ộ.