Review đoạn code sau:
import React from "react";
import ReactDOM from "react-dom";
import { Input } from "reactstrap";
import "./styles.css";
class CheckBox extends React.Component {
render() {
return <Layer1 checked={this.props.checked} onCheck={this.props.onCheck} />;
}
}
const Layer1 = ({ checked, onCheck }) => (
<Layer2 checked={checked} onCheck={onCheck} />
);
const Layer2 = ({ checked, onCheck }) => (
<React.Fragment>
{checked ? "Btn checked" : "Btn unchecked"}
<Layer3 checked={checked} onCheck={onCheck} />
</React.Fragment>
);
const Layer3 = ({ checked, onCheck }) => (
<Layer4 checked={checked} onCheck={onCheck} />
);
const Layer4 = ({ checked, onCheck }) => (
<Input type="checkbox" checked={checked} onClick={onCheck} />
);
class App extends React.Component {
state = { checked: false };
onCheck = () => this.setState(({ checked }) => ({ checked: !checked }));
render() {
return <CheckBox checked={this.state.checked} onCheck={this.onCheck} />;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Trong trường hợp này: việc truyền props checked, onCheck từ App component đến Layer4 component được gọi là Props drilling. Rất có thể lúc mới bắt đầu code React bạn cũng đã từng code như trên. :))
Và hãy thử tưởng tượng xem, sẽ ra sao nếu 1 ngày:
- Layer4 component không cần checked props nữa.
- Sau vài ngày suy nghĩ thì vẫn nên cần checked props.
- Hoặc checked đặt tên không hợp lý đổi lại thành check thì hợp lý hơn.
- …
Hahaha, nếu mà điều đó xảy ra thật thì mình cá là sẽ có khối người muốn chửi thề đó :))).
Và để khắc phục được vấn đề trên ta có thể dùng API mới mà React đã cung cấp tên là Context. Child components có thể lấy được props từ parent component mà không cần phải thông qua các lớp khác nhau như trên.
Các bước thực hiện:
Bước 1: Khởi tạo CheckBoxContext thông qua hàm React.createContext()
const CheckBoxContext = React.createContext({
checked: false,
onCheck: () => {}
});
Bước 2: Khai báo các props cần truyền thông qua Provider.
<CheckBoxContext.Provider value={this.state} {...this.props}>
<Layer1 />
</CheckBoxContext.Provider>
Bước 3. Nhận props thông qua Consumer.
<CheckBox.Consumer>
{({ checked, onCheck }) => (
<Input type="checkbox" checked={checked} onClick={onCheck} />
)}
</CheckBox.Consumer>
Full code:
import React from "react";
import ReactDOM from "react-dom";
import { Input } from "reactstrap";
import "./styles.css";
const CheckBoxContext = React.createContext({
checked: false,
onCheck: () => {}
});
class CheckBox extends React.Component {
static Consumer = CheckBoxContext.Consumer;
onCheck = () => this.setState(({ checked }) => ({ checked: !checked }));
state = {
checked: false,
onCheck: this.onCheck
};
render() {
return (
<CheckBoxContext.Provider value={this.state} {...this.props}>
<Layer1 />
</CheckBoxContext.Provider>
);
}
}
const Layer1 = () => <Layer2 />;
const Layer2 = () => (
<CheckBox.Consumer>
{({ checked }) => (
<React.Fragment>
{checked ? "Btn checked" : "Btn unchecked"}
<Layer3 />
</React.Fragment>
)}
</CheckBox.Consumer>
);
const Layer3 = () => <Layer4 />;
const Layer4 = () => (
<CheckBox.Consumer>
{({ checked, onCheck }) => (
<Input type="checkbox" checked={checked} onClick={onCheck} />
)}
</CheckBox.Consumer>
);
class App extends React.Component {
render() {
return <CheckBox />;
}
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
