javascript - Làm thế nào để lấy giá trị của một trường nhập liệu sử dụng ReactJS?
Tôi có thành phần React sau:
export default class MyComponent extends React.Component {
onSubmit(e) {
e.preventDefault();
var title = this.title;
console.log(title);
}
render(){
return (
...
<form className="form-horizontal">
...
<input type="text" className="form-control" ref={(c) => this.title = c} name="title" />
...
</form>
...
<button type="button" onClick={this.onSubmit} className="btn">Save</button>
...
);
}
};
Bảng điều khiển đang trả về undefined - có ai biết vấn đề gì với đoạn mã này không?
Câu trả lời #1
Có ba câu trả lời ở đây, tùy thuộc vào phiên bản React mà bạn đang (bắt buộc phải) làm việc, và liệu bạn có muốn sử dụng hooks hay không.
Trước tiên:
Điều quan trọng là phải hiểu cách React hoạt động, để bạn có thể làm mọi việc một cách đúng đắn (mẹo chuyên nghiệp: việc chạy qua các hướng dẫn React trên trang web React rất đáng giá. Chúng được viết tốt và bao quát tất cả các cơ bản theo cách thực sự giải thích cách làm). "Đúng đắn" ở đây có nghĩa là bạn không chỉ viết một trang web, bạn đang viết giao diện người dùng cho một ứng dụng mà tình cờ được hiển thị trong trình duyệt; tất cả công việc thực sự về giao diện người dùng diễn ra trong React, không phải trong "những gì bạn quen thuộc khi viết một trang web" (đây là lý do các ứng dụng React thực sự là "ứng dụng", không phải "trang web").
Các ứng dụng React được hiển thị dựa trên hai yếu tố:
- các thuộc tính của thành phần như được khai báo bởi bất kỳ cha mẹ nào tạo ra một thể hiện của thành phần đó, mà cha mẹ có thể sửa đổi trong suốt vòng đời của nó, và
- trạng thái nội bộ riêng của thành phần, mà nó có thể tự thay đổi trong suốt vòng đời của chính nó.
Điều bạn không làm một cách rõ ràng khi sử dụng React là tạo các phần tử HTML rồi sau đó sử dụng chúng: khi bạn bảo React sử dụng một <input>, ví dụ, bạn không tạo một phần tử input HTML, thay vào đó bạn đang bảo React tạo một đối tượng input của React mà khi biên dịch ứng dụng React của bạn cho web, đối tượng này sẽ hiển thị dưới dạng một phần tử input HTML, với việc xử lý sự kiện được kiểm soát bởi React.
Khi sử dụng React, những gì bạn đang làm là tạo ra các thành phần giao diện người dùng của ứng dụng hiển thị dữ liệu cho người dùng (thường có thể thao tác được), với tương tác của người dùng thay đổi trạng thái của ứng dụng theo cách mà bạn định nghĩa - các hành động do người dùng thực hiện có thể cập nhật props hoặc state của một thành phần, mà React sử dụng như một tín hiệu để tạo ra một biểu diễn giao diện người dùng mới cho các thành phần đã thay đổi, điều này có thể gây ra việc cập nhật một phần giao diện của ứng dụng để phản ánh trạng thái mới.
Trong mô hình lập trình này, trạng thái nội bộ của ứng dụng là cơ quan quyết định cuối cùng, thay vì "giao diện người dùng mà người dùng của bạn nhìn thấy và tương tác": nếu một người dùng cố gắng nhập điều gì đó vào một trường nhập liệu, và bạn không viết gì để xử lý điều đó, sẽ không có gì xảy ra: giao diện người dùng là sự phản ánh của trạng thái ứng dụng, không phải ngược lại. Thực tế, DOM của trình duyệt gần như chỉ là một suy nghĩ phụ trong mô hình lập trình này: nó chỉ tình cờ là một khung giao diện người dùng cực kỳ tiện lợi mà hầu hết mọi người trên hành tinh chắc chắn có thể truy cập (nhưng nó không phải là công cụ duy nhất mà React biết cách làm việc cùng).
Một ví dụ cụ thể
Với điều đó đã được đề cập, hãy xem cách một người dùng tương tác với một phần tử input trong React. Trước tiên, chúng ta cần có một phần tử giao diện người dùng để người dùng tương tác:
- Bạn đã viết một thành phần để quản lý (tức là cả lưu trữ và hiển thị) một số dữ liệu chuỗi cho người dùng của bạn, với một hàm
onChangeđể xử lý dữ liệu của người dùng. - Mã kết xuất của thành phần của bạn được React sử dụng để tạo một DOM ảo chứa một thành phần
input(không phải một phần tử DOM<input>), và liên kết bộ xử lýonChangecủa bạn với thành phần đó để nó có thể được gọi với dữ liệu sự kiện của React (vì vậy hãy lưu ý rằng đây không phải là trình nghe sự kiệnchangecủa DOM, và không nhận được một dữ liệu sự kiện giống như các trình nghe sự kiện DOM thông thường). - Thư viện React sau đó sẽ chuyển đổi DOM ảo đó thành một giao diện người dùng mà người dùng có thể tương tác, và nó sẽ được cập nhật khi trạng thái ứng dụng thay đổi. Vì nó chạy trong trình duyệt, nó sẽ tạo ra một phần tử nhập liệu HTML.
Sau đó, người dùng của bạn thực sự cố gắng tương tác với phần tử nhập liệu đó:
- Người dùng của bạn nhấp vào phần tử đầu vào và bắt đầu gõ.
- Chưa có điều gì xảy ra với phần tử nhập liệu. Thay vào đó, các sự kiện nhập liệu bị React chặn và loại bỏ ngay lập tức.
- React biến sự kiện trên trình duyệt thành một sự kiện React, và gọi hàm
onChangecho thành phần DOM ảo với dữ liệu sự kiện React. - Chức năng đó có thể làm gì đó, dựa trên cách bạn viết nó, và trong trường hợp này bạn gần như chắc chắn đã viết nó để cập nhật trạng thái của thành phần của bạn với những gì người dùng (đã cố gắng) nhập.
- Nếu một cập nhật state được lập lịch, React sẽ chạy cập nhật state đó trong tương lai gần, điều này sẽ kích hoạt một lần render sau khi cập nhật.
- Trong quá trình render pass, nó kiểm tra xem trạng thái có thực sự khác nhau hay không, và nếu có, nó sẽ tạo ra một DOM ảo tạm thời thứ hai, sau đó so sánh với (một phần của) DOM ảo của ứng dụng của bạn, xác định tập hợp các thao tác thêm/cập nhật/xóa mà nó cần thực hiện trên DOM ảo của ứng dụng của bạn để nó nhìn giống như DOM tạm thời mới, sau đó áp dụng các thao tác đó và bỏ DOM ảo tạm thời đi.
- Sau đó nó cập nhật giao diện người dùng để phản ánh những gì mà DOM ảo bây giờ trông như thế nào.
- Và sau tất cả những điều đó, cuối cùng chúng ta cũng có một DOM được cập nhật trên trang mà người dùng thực sự đang xem, và họ thấy những gì họ đã gõ vào phần tử nhập liệu.
Vì vậy, điều này hoàn toàn khác với mô hình trình duyệt thông thường: thay vì người dùng cập nhật dữ liệu giao diện bằng cách nhập vào một ô văn bản trước và mã của chúng tôi đọc "giá trị hiện tại của ô văn bản đó" để xác định trạng thái sau, React đã biết trạng thái là gì, và sử dụng sự kiện để cập nhật trạng thái trước, dẫn đến việc giao diện được cập nhật sau.
Và điều quan trọng cần nhớ là tất cả những việc này diễn ra gần như ngay lập tức, vì vậy đối với người dùng của bạn, nó trông như thể họ đã gõ văn bản vào một phần tử nhập liệu theo cùng cách họ sẽ làm với bất kỳ trang web nào khác, nhưng bên trong thì mọi thứ hoàn toàn khác mặc dù vẫn dẫn đến cùng một kết quả.
Vậy, với điều đó đã được đề cập, hãy xem cách lấy giá trị từ các phần tử trong React:
Các lớp thành phần và ES6 (React 16+ và quá trình chuyển tiếp 15.5)
Kể từ React 16 (và bắt đầu một cách nhẹ nhàng từ 15.5), việc gọi createClass không còn được hỗ trợ nữa, và cần phải sử dụng cú pháp class. Điều này thay đổi hai thứ: cú pháp class rõ ràng, nhưng cũng ảnh hưởng đến việc ràng buộc ngữ cảnh this mà createClass có thể làm "một cách miễn phí", vì vậy để đảm bảo mọi thứ vẫn hoạt động, hãy chắc chắn rằng bạn đang sử dụng cú pháp "fat arrow" cho các hàm ẩn danh giữ ngữ cảnh this trong các trình xử lý onWhatever, chẳng hạn như onChange mà chúng tôi sử dụng trong đoạn mã ở đây:
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.reset();
}
reset() {
// Always set the initial state in its own function, so that
// you can trivially reset your components at any point.
this.state = {
inputValue: ''
};
}
render() {
return (
// ...
<input value={this.state.inputValue} onChange={evt => this.updateInputValue(evt)}/>
// ...
);
},
updateInputValue(evt) {
const val = evt.target.value;
// ...
this.setState({
inputValue: val
});
}
});
Bạn cũng có thể đã thấy người ta sử dụng bind trong hàm tạo của họ cho tất cả các chức năng xử lý sự kiện, như sau:
constructor(props) {
super(props);
this.handler = this.handler.bind(this);
...
}
render() {
return (
...
<element onclick={this.handler}/>
...
);
}
Đừng làm vậy.
Hầu như bất cứ khi nào bạn sử dụng bind, câu nói nổi tiếng "bạn đang làm sai" đều áp dụng. Lớp của bạn đã định nghĩa prototype của đối tượng, và do đó đã định nghĩa ngữ cảnh của instance. Đừng áp dụng bind lên trên điều đó; hãy sử dụng việc chuyển tiếp sự kiện bình thường thay vì sao chép tất cả các lời gọi hàm của bạn trong constructor, vì việc sao chép đó làm tăng bề mặt lỗi và khiến việc truy tìm lỗi khó hơn nhiều vì vấn đề có thể nằm trong constructor của bạn thay vì nơi bạn gọi mã của mình.
"Nhưng rồi nó liên tục tạo và loại bỏ các hàm khi render lại!" và điều đó có thể đúng nhưng bạn sẽ không nhận thấy. Người dùng của bạn cũng vậy. Nếu việc thu gom rác các trình xử lý sự kiện là nút thắt hiệu suất của bạn, thì đã có quá nhiều điều sai lầm xảy ra đến mức bạn cần dừng lại và suy nghĩ lại thiết kế của mình: lý do React hoạt động cực kỳ tốt là vì nó không cập nhật toàn bộ giao diện người dùng, nó chỉ cập nhật các phần thay đổi, và trong một giao diện được thiết kế tốt, thời gian mà phần lớn giao diện của bạn không thay đổi một cách đáng kể vượt xa thời gian mà các phần nhỏ của giao diện của bạn dành để cập nhật.
Các thành phần hàm với hooks (React 16.8+)
Từ React 16.8 trở đi, component dạng hàm (tức là chỉ đơn giản là một hàm nhận một số props làm đối số) cũng có thể được sử dụng như thể nó là một thể hiện của lớp component, mà không cần phải viết lớp, và cũng có thể có trạng thái thông qua việc sử dụng hooks.
Nếu bạn không cần toàn bộ mã lớp và một hàm instance duy nhất là đủ, thì bây giờ bạn có thể sử dụng hook useState để có một biến trạng thái duy nhất và hàm cập nhật của nó, hoạt động tương tự như các ví dụ trên, ngoại trừ việc không sử dụng lệnh gọi hàm setState "toàn diện" và sử dụng một bộ setter trạng thái riêng cho mỗi giá trị mà bạn đang làm việc:
import { useId, useState } from 'react';
function myFunctionalComponentFunction(props) {
const id = useId();
const [input, setInput] = useState(props?.value ?? '');
return (
<div>
<label htmlFor={id}>Please specify:</label>
<input id={id} value={input} onInput={e => setInput(e.target.value)}/>
</div>
);
}
Trước đây, sự phân biệt không chính thức giữa các lớp và các thành phần hàm là "các thành phần hàm không có state", vì vậy chúng ta không thể dựa vào điều đó nữa: sự khác biệt giữa các thành phần hàm và các thành phần lớp có thể được tìm thấy trải dài trên nhiều trang trong tài liệu React rất được viết tốt (không có giải thích một dòng tóm tắt tiện lợi để bạn hiểu sai!) mà bạn nên đọc để biết mình đang làm gì và từ đó có thể biết liệu bạn có chọn giải pháp tốt nhất (bất kể điều đó có nghĩa là gì đối với bạn) để tự lập trình giải quyết vấn đề bạn đang gặp phải.
React 15 trở xuống, sử dụng ES5 kế thừa và createClass
Để làm mọi thứ một cách đúng đắn, thành phần của bạn có một giá trị trạng thái, giá trị này được hiển thị qua một trường nhập liệu, và chúng ta có thể cập nhật nó bằng cách làm cho phần tử giao diện người dùng đó gửi các sự kiện thay đổi trở lại thành phần:
var Component = React.createClass({
getInitialState: function() {
return {
inputValue: ''
};
},
render: function() {
return (
//...
<input value={this.state.inputValue} onChange={this.updateInputValue}/>
//...
);
},
updateInputValue: function(evt) {
this.setState({
inputValue: evt.target.value
});
}
});
Vì vậy, chúng ta bảo React sử dụng hàm updateInputValue để xử lý tương tác của người dùng, sử dụng setState để lên lịch cập nhật trạng thái, và việc render truy cập vào this.state.inputValue có nghĩa là khi nó được render lại sau khi cập nhật trạng thái, người dùng sẽ thấy văn bản được cập nhật dựa trên những gì họ đã nhập.
phần phụ lục dựa trên các nhận xét
Vì các đầu vào UI đại diện cho các giá trị trạng thái (hãy xem xét điều gì sẽ xảy ra nếu một người dùng đóng tab giữa chừng, và tab đó được khôi phục. Liệu tất cả các giá trị họ đã điền có nên được khôi phục không? Nếu có, đó là trạng thái). Điều đó có thể khiến bạn cảm thấy rằng một biểu mẫu lớn cần hàng chục hoặc thậm chí hàng trăm trường nhập liệu, nhưng React là về cách mô hình hóa UI của bạn theo cách dễ bảo trì: bạn không có 100 trường nhập liệu độc lập, bạn có các nhóm đầu vào liên quan, vì vậy bạn nắm bắt mỗi nhóm trong một thành phần và sau đó xây dựng biểu mẫu "chính" của mình như một tập hợp các nhóm.
MyForm:
render:
<PersonalData/>
<AppPreferences/>
<ThirdParty/>
...
Điều này cũng dễ dàng bảo trì hơn nhiều so với một thành phần biểu mẫu khổng lồ. Chia nhỏ các nhóm thành các thành phần với việc quản lý trạng thái, nơi mỗi thành phần chỉ chịu trách nhiệm theo dõi một vài trường nhập liệu cùng một lúc.
Bạn cũng có thể cảm thấy rằng việc viết ra tất cả những đoạn mã đó là "phiền toái", nhưng đó là một sự tiết kiệm sai lầm: các nhà phát triển không phải là bạn, bao gồm cả bạn trong tương lai, thực sự được lợi rất nhiều từ việc thấy tất cả các đầu vào đó được kết nối rõ ràng, vì nó làm cho các đường đi của mã dễ theo dõi hơn nhiều. Tuy nhiên, bạn luôn có thể tối ưu hóa. Ví dụ, bạn có thể viết một bộ liên kết trạng thái
MyComponent = React.createClass({
getInitialState() {
return {
firstName: this.props.firstName || "",
lastName: this.props.lastName || ""
...: ...
...
}
},
componentWillMount() {
Object.keys(this.state).forEach(n => {
let fn = n + 'Changed';
this[fn] = evt => {
let update = {};
update[n] = evt.target.value;
this.setState(update);
});
});
},
render: function() {
return Object.keys(this.state).map(n => {
<input
key={n}
type="text"
value={this.state[n]}
onChange={this[n + 'Changed']}/>
});
}
});
Câu trả lời #2
Đã quản lý để lấy giá trị trường nhập bằng cách làm điều gì đó như thế này:
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props);
this.state = {
username : ''
}
this.updateInput = this.updateInput.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
updateInput(event){
this.setState({username : event.target.value})
}
handleSubmit(){
console.log('Your input value is: ' + this.state.username)
//Send state to the server code
}
render(){
return (
<div>
<input type="text" onChange={this.updateInput}></input>
<input type="submit" onClick={this.handleSubmit} ></input>
</div>
);
}
}
//output
//Your input value is: x
Câu trả lời số 3
Trong React 16, tôi sử dụng
<Input id="number"
type="time"
onChange={(evt) => { console.log(evt.target.value); }} />
Câu trả lời #4
Bạn nên sử dụng constructor dưới lớp MyComponent kế thừa React.Component
constructor(props){
super(props);
this.onSubmit = this.onSubmit.bind(this);
}
Sau đó bạn sẽ nhận được kết quả của tiêu đề
Câu trả lời số 5
Trong Thành Phần Hàm
useState
Trả về một giá trị có trạng thái và một hàm để cập nhật nó. Trong lần render ban đầu, trạng thái trả về (state) giống với giá trị được truyền như đối số đầu tiên (initialState). Hàm setState được sử dụng để cập nhật trạng thái. Nó nhận một giá trị trạng thái mới và đưa vào hàng đợi để render lại component.
src ---> https://reactjs.org/docs/hooks-reference.html#usestate
useRef
useRef trả về một đối tượng ref có thể thay đổi mà thuộc tính .current của nó được khởi tạo với đối số truyền vào (initialValue). Đối tượng trả về sẽ tồn tại trong suốt vòng đời của component.
src ---> https://reactjs.org/docs/hooks-reference.html#useref
import { useRef, useState } from "react";
export default function App() {
const [val, setVal] = useState('');
const inputRef = useRef();
const submitHandler = (e) => {
e.preventDefault();
setVal(inputRef.current.value);
}
return (
<div className="App">
<form onSubmit={submitHandler}>
<input ref={inputRef} />
<button type="submit">Submit</button>
</form>
<p>Submit Value: <b>{val}</b></p>
</div>
);
}
Câu trả lời số 6
Đặt cho <input> một id duy nhất
<input id='title' ...>
và sau đó sử dụng Web API tiêu chuẩn để tham chiếu nó trong DOM
const title = document.getElementById('title').value
Không cần liên tục cập nhật trạng thái React với mỗi lần nhấn phím. Chỉ cần lấy giá trị khi cần thiết.
