2019: thử hook + hứa hẹn thảo luận
Đây là phiên bản cập nhật nhất về cách tôi sẽ giải quyết vấn đề này. Tôi sẽ dùng:
Đây là một số hệ thống dây ban đầu nhưng bạn đang tự mình soạn các khối nguyên thủy và bạn có thể tự tạo móc tùy chỉnh để bạn chỉ cần thực hiện việc này một lần.
// Generic reusable hook
const useDebouncedSearch = (searchFunction) => {
// Handle the input text state
const [inputText, setInputText] = useState('');
// Debounce the original search async function
const debouncedSearchFunction = useConstant(() =>
AwesomeDebouncePromise(searchFunction, 300)
);
// The async callback is run each time the text changes,
// but as the search function is debounced, it does not
// fire a new request on each keystroke
const searchResults = useAsync(
async () => {
if (inputText.length === 0) {
return [];
} else {
return debouncedSearchFunction(inputText);
}
},
[debouncedSearchFunction, inputText]
);
// Return everything needed for the hook consumer
return {
inputText,
setInputText,
searchResults,
};
};
Và sau đó bạn có thể sử dụng hook của mình:
const useSearchStarwarsHero = () => useDebouncedSearch(text => searchStarwarsHeroAsync(text))
const SearchStarwarsHeroExample = () => {
const { inputText, setInputText, searchResults } = useSearchStarwarsHero();
return (
<div>
<input value={inputText} onChange={e => setInputText(e.target.value)} />
<div>
{searchResults.loading && <div>...</div>}
{searchResults.error && <div>Error: {search.error.message}</div>}
{searchResults.result && (
<div>
<div>Results: {search.result.length}</div>
<ul>
{searchResults.result.map(hero => (
<li key={hero.name}>{hero.name}</li>
))}
</ul>
</div>
)}
</div>
</div>
);
};
Bạn sẽ tìm thấy ví dụ này chạy ở đây và bạn nên đọc tài liệu phản ứng-async-hook để biết thêm chi tiết.
2018: thử hứa hẹn thảo luận
Chúng tôi thường muốn gỡ bỏ các lệnh gọi API để tránh làm ngập phần phụ trợ với các yêu cầu vô dụng.
Năm 2018, làm việc với các cuộc gọi lại (Lodash / Underscore) cảm thấy tồi tệ và dễ bị lỗi đối với tôi. Rất dễ gặp phải các vấn đề về nồi hơi và đồng thời do các lệnh gọi API giải quyết theo thứ tự tùy ý.
Tôi đã tạo ra một thư viện nhỏ với React trong tâm trí để giải quyết những khó khăn của bạn: lời hứa tuyệt vời .
Điều này không nên phức tạp hơn thế:
const searchAPI = text => fetch('/search?text=' + encodeURIComponent(text));
const searchAPIDebounced = AwesomeDebouncePromise(searchAPI, 500);
class SearchInputAndResults extends React.Component {
state = {
text: '',
results: null,
};
handleTextChange = async text => {
this.setState({ text, results: null });
const result = await searchAPIDebounced(text);
this.setState({ result });
};
}
Hàm được gỡ lỗi đảm bảo rằng:
- Các lệnh gọi API sẽ được công bố
- chức năng được công bố luôn trả lại một lời hứa
- chỉ lời hứa được trả lại của cuộc gọi cuối cùng sẽ giải quyết
- một lần duy nhất
this.setState({ result });
sẽ xảy ra cho mỗi cuộc gọi API
Cuối cùng, bạn có thể thêm một mẹo khác nếu thành phần của bạn ngắt kết nối:
componentWillUnmount() {
this.setState = () => {};
}
Lưu ý rằng Đài quan sát (RxJS) cũng có thể rất phù hợp để gỡ lỗi đầu vào, nhưng đó là một sự trừu tượng mạnh mẽ hơn có thể khó học / sử dụng chính xác hơn.
<2017: vẫn muốn sử dụng gỡ lỗi gọi lại?
Phần quan trọng ở đây là tạo một hàm được gỡ lỗi (hoặc điều chỉnh) cho mỗi thể hiện thành phần . Bạn không muốn tạo lại chức năng gỡ lỗi (hoặc van tiết lưu) mọi lúc và bạn không muốn nhiều trường hợp chia sẻ cùng một chức năng được gỡ lỗi.
Tôi không xác định chức năng thảo luận trong câu trả lời này vì nó không thực sự phù hợp, nhưng câu trả lời này sẽ hoạt động hoàn toàn tốt với _.debounce
dấu gạch dưới hoặc dấu gạch ngang, cũng như bất kỳ chức năng gỡ lỗi nào do người dùng cung cấp.
Ý TƯỞNG TỐT:
Vì các hàm bị gỡ lỗi là trạng thái, chúng ta phải tạo một hàm bị lỗi cho mỗi thể hiện thành phần .
ES6 (thuộc tính lớp) : được đề xuất
class SearchBox extends React.Component {
method = debounce(() => {
...
});
}
ES6 (lớp xây dựng)
class SearchBox extends React.Component {
constructor(props) {
super(props);
this.method = debounce(this.method.bind(this),1000);
}
method() { ... }
}
ES5
var SearchBox = React.createClass({
method: function() {...},
componentWillMount: function() {
this.method = debounce(this.method.bind(this),100);
},
});
Xem JsFiddle : 3 trường hợp đang tạo 1 mục nhập nhật ký cho mỗi phiên bản (tạo 3 mục trên toàn cầu).
Không phải là một ý tưởng tốt:
var SearchBox = React.createClass({
method: function() {...},
debouncedMethod: debounce(this.method, 100);
});
Nó sẽ không hoạt động, bởi vì trong quá trình tạo đối tượng mô tả lớp, this
không phải là đối tượng tự tạo. this.method
không trả về những gì bạn mong đợi vì this
bối cảnh không phải là chính đối tượng (mà thực sự chưa thực sự tồn tại BTW vì nó mới được tạo ra).
Không phải là một ý tưởng tốt:
var SearchBox = React.createClass({
method: function() {...},
debouncedMethod: function() {
var debounced = debounce(this.method,100);
debounced();
},
});
Lần này, bạn đang tạo một hàm có hiệu quả gọi là của bạn this.method
. Vấn đề là bạn đang tạo lại nó trên mỗi debouncedMethod
cuộc gọi, vì vậy chức năng gỡ lỗi mới được tạo không biết gì về các cuộc gọi trước đây! Bạn phải sử dụng lại chức năng đã được công bố theo thời gian hoặc việc gỡ lỗi sẽ không xảy ra.
Không phải là một ý tưởng tốt:
var SearchBox = React.createClass({
debouncedMethod: debounce(function () {...},100),
});
Đây là một chút khó khăn ở đây.
Tất cả các cá thể được gắn kết của lớp sẽ chia sẻ cùng một chức năng đã được công bố và thường thì đây không phải là điều bạn muốn!. Xem JsFiddle : 3 trường hợp chỉ tạo ra 1 mục nhật ký trên toàn cầu.
Bạn phải tạo một hàm bị gỡ lỗi cho từng thể hiện thành phần , và không phải là một hàm bị gỡ lỗi duy nhất ở cấp độ lớp, được chia sẻ bởi mỗi thể hiện thành phần.
Chăm sóc tổng hợp sự kiện của React
Điều này có liên quan vì chúng ta thường muốn gỡ lỗi hoặc điều tiết các sự kiện DOM.
Trong React, các đối tượng sự kiện (tức là SyntheticEvent
) mà bạn nhận được trong các cuộc gọi lại được gộp lại (điều này hiện được ghi lại ). Điều này có nghĩa là sau khi gọi lại sự kiện đã được gọi, thì TotalEvent mà bạn nhận được sẽ được đưa trở lại vào nhóm với các thuộc tính trống để giảm áp lực GC.
Vì vậy, nếu bạn truy cập SyntheticEvent
các thuộc tính không đồng bộ vào cuộc gọi lại ban đầu (có thể là trường hợp nếu bạn điều tiết / gỡ lỗi), các thuộc tính bạn truy cập có thể bị xóa. Nếu bạn muốn sự kiện không bao giờ được đưa trở lại vào nhóm, bạn có thể sử dụng persist()
phương thức này.
Không tồn tại (hành vi mặc định: sự kiện gộp)
onClick = e => {
alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
setTimeout(() => {
alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
}, 0);
};
Thứ 2 (không đồng bộ) sẽ in hasNativeEvent=false
vì các thuộc tính sự kiện đã được dọn sạch.
Với sự kiên trì
onClick = e => {
e.persist();
alert(`sync -> hasNativeEvent=${!!e.nativeEvent}`);
setTimeout(() => {
alert(`async -> hasNativeEvent=${!!e.nativeEvent}`);
}, 0);
};
Thứ 2 (không đồng bộ) sẽ in hasNativeEvent=true
vìpersist
cho phép bạn tránh đưa sự kiện trở lại nhóm.
Bạn có thể kiểm tra 2 hành vi này tại đây: JsFiddle
Đọc câu trả lời của Julen để biết ví dụ về việc sử dụng persist()
chức năng tiết lưu / gỡ lỗi.
debounce
. ở đây, khi nàoonChange={debounce(this.handleOnChange, 200)}/>
, nó sẽ gọidebounce function
mỗi lần nhưng, trên thực tế, cái chúng ta cần là gọi hàm mà hàm gỡ lỗi trả về.