メモの内容は、AcWing の Web アプリケーション コース配布資料 (コース リンク: AcWing Web アプリケーション コース )から転載されています。
コンテンツ
このセクションの内容は、異なるコンポーネントを使用して DOM ツリーを形成する、異なるコンポーネントにデータを渡す、異なるコンポーネントのメソッドを呼び出すなどのコンポーネントの組み合わせと、異なるコンポーネントのライフサイクルです。
1. 親コンポーネントを作成する
前のコンポーネントBox
の作業を続けます。まずBoxes
、一連のBox
コンポーネントを含むコンポーネントを作成します。
components
ディレクトリに作成しますboxes.jsx
:
import React, {
Component } from 'react';
class Boxes extends Component {
state = {
}
render() {
return (
<h1>Boxes</h1>
);
}
}
export default Boxes;
次に、それを変更しますindex.js
。
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import 'bootstrap/dist/css/bootstrap.css';
import Boxes from './components/boxes';
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<Boxes />);
次に、Boxes
に複数の要素を追加しますBox
。コンポーネントに複数の並列要素が含まれる場合、それらをタグで囲む必要があります。React では仮想タグを使用できます<React.Fragment>
。
import React, {
Component } from 'react';
import Box from './box';
class Boxes extends Component {
state = {
}
render() {
return (
<React.Fragment>
<Box />
<Box />
<Box />
</React.Fragment>
);
}
}
export default Boxes;
便宜上、配列で表すこともでき、 のBox
情報は に保存されますstate
。React コンポーネントには複数の子があり、それぞれのkey
ニーズが異なるため、一意の子を保存する必要がありますid
。
import React, {
Component } from 'react';
import Box from './box';
class Boxes extends Component {
state = {
boxes: [
{
id: 1, x: 0},
{
id: 2, x: 0},
{
id: 3, x: 0},
{
id: 4, x: 0},
]
}
render() {
return (
<React.Fragment>
{
this.state.boxes.map(box => (
<Box key={
box.id} />
))}
</React.Fragment>
);
}
}
export default Boxes;
2. データを上から下に渡す
データは属性を通じてthis.props
上から下に渡すことができます。たとえば、次のようBoxes
に渡しますx
。
...
class Boxes extends Component {
state = {
...
}
render() {
return (
<React.Fragment>
{
this.state.boxes.map(box => (
<Box key={
box.id} x={
box.x} name='yyj' />
))}
</React.Fragment>
);
}
}
export default Boxes;
Box
情報はView contentに出力できますconsole.log(this.props);
。
修正Box
中x
:
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
state = {
x: this.props.x,
};
...
}
export default Box;
3. 子ノードを渡す
次の形式でタグを記述し<Box></Box>
、そのタグにサブタグを追加できます。
import React, {
Component } from 'react';
import Box from './box';
class Boxes extends Component {
state = {
boxes: [
{
id: 1, x: 0},
{
id: 2, x: 0},
{
id: 3, x: 0},
{
id: 4, x: 0},
]
}
render() {
return (
<React.Fragment>
{
this.state.boxes.map(box => (
<Box key={
box.id} x={
box.x} name='yyj'>
<h1>Title</h1>
</Box>
))}
</React.Fragment>
);
}
}
export default Boxes;
このようにして、this.props
属性が 1 つ増えchildren
、それを使用して[]
サブタグに個別にアクセスできるようになります。この渡された値はどこにでも定義できます。たとえば、Box
各コンポーネントの先頭に配置できます。
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
state = {
x: this.props.x,
};
handleClickLeft = (step) => {
this.setState({
x: this.state.x - step
});
}
handleClickRight = (step) => {
this.setState({
x: this.state.x + step
});
}
handleClickLeftTmp = () => {
this.handleClickLeft(10);
}
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
console.log(this.props);
return (
// HTML标签中可以使用{}写一个表达式
<React.Fragment>
{
this.props.children}
<div style={
this.getStyles()}>{
this.state.x}</div>
<button onClick={
this.handleClickLeftTmp} className='btn btn-primary m-2'>Left</button>
<button onClick={
() => this.handleClickRight(10)} className='btn btn-success m-2'>Right</button>
</React.Fragment>
);
}
getStyles() {
...
}
}
export default Box;
4. 関数を下から上に呼び出す
親要素はthis.props
を介して子要素に情報を渡すことができ、子要素は関数を使用して親要素に情報を渡すこともできます。削除ボタン をクリックして誰かを削除する必要がありBox
、その情報は に保存されていますがBoxes
、state
クリックによってトリガーされたイベントは にあるとしますBox
(注: 各コンポーネントはthis.state
コンポーネント内でのみ変更でき、他のコンポーネントは変更できません)。
親要素で関数を定義し、その関数を子要素に渡すことができます。
import React, {
Component } from 'react';
import Box from './box';
class Boxes extends Component {
state = {
boxes: [
{
id: 1, x: 0},
{
id: 2, x: 0},
{
id: 3, x: 0},
{
id: 4, x: 0},
]
}
handleDelete = (boxId) => {
// 遍历一遍state.boxes,将box.id不为传入的参数boxId的数据保留下来
const res = this.state.boxes.filter(box => box.id !== boxId);
this.setState({
boxes: res});
}
render() {
return (
<React.Fragment>
{
this.state.boxes.map(box => (
<Box key={
box.id} id={
box.id} x={
box.x} name='yyj'
onDelete={
this.handleDelete}
/>
))}
</React.Fragment>
);
}
}
export default Boxes;
このようにして、子要素は親要素を操作する関数を呼び出すことができます。
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
...
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
console.log(this.props);
return (
// HTML标签中可以使用{}写一个表达式
<React.Fragment>
...
<button onClick={
() => this.props.onDelete(this.props.id)} className='btn btn-danger m-2'>Delete</button>
</React.Fragment>
);
}
getStyles() {
...
}
}
export default Box;
ここで、すべてをBoxes
クリアするために [リセット] ボタンを実装します。Box
x
import React, {
Component } from 'react';
import Box from './box';
class Boxes extends Component {
state = {
boxes: [
{
id: 1, x: 0},
{
id: 2, x: 1},
{
id: 3, x: 2},
{
id: 4, x: 3},
]
}
handleDelete = (boxId) => {
...
}
handleReset = () => {
const res = this.state.boxes.map(box => {
return {
id: box.id,
x: 0,
}
});
this.setState({
boxes: res});
}
render() {
console.log(this.state.boxes);
return (
<React.Fragment>
<button
onClick={
this.handleReset}
style={
{
marginBottom: '15px'}}
className='btn btn-info'
>Reset</button>
{
this.state.boxes.map(box => (
<Box key={
box.id} id={
box.id} x={
box.x} name='yyj'
onDelete={
this.handleDelete}
/>
))}
</React.Fragment>
);
}
}
export default Boxes;
x
コンソールで観察すると、[リセット] ボタンをクリックした後、 は確かにゼロに設定されていますが、Box
表示は変化していないことがわかります。x
これは、値を外部から変更できないためです。そのため、 in を削除state
でき、レンダリングする必要があります。コンポーネント内の外部値。Box
state
state
保持されている各データは 1 つの にのみ保存できます。関数によって変更が上書きされる可能性があるため、this.state
の値を直接変更しないでください。this.state
setState
前の操作で使用した関数を変更しBoxes
て転送します。Box
state
import React, {
Component } from 'react';
import Box from './box';
class Boxes extends Component {
state = {
boxes: [
{
id: 1, x: 0},
{
id: 2, x: 1},
{
id: 3, x: 2},
{
id: 4, x: 3},
]
}
handleDelete = (boxId) => {
// 遍历一遍state.boxes,将box.id不为传入的参数boxId的数据保留下来
const res = this.state.boxes.filter(box => box.id !== boxId);
this.setState({
boxes: res});
}
handleReset = () => {
const res = this.state.boxes.map(box => {
return {
id: box.id,
x: 0,
}
});
this.setState({
boxes: res});
}
// 需要知道修改的是哪个box
handleClickLeft = (box) => {
const boxes = [...this.state.boxes]; // 浅拷贝一份
const k = boxes.indexOf(box); // 传入的box是引用,找出其在boxes中的下标k
boxes[k] = {
...boxes[k]}; // 再clone一遍,相当于创建新的state,深拷贝
boxes[k].x--;
this.setState({
boxes: boxes});
}
handleClickRight = (box) => {
const boxes = [...this.state.boxes];
const k = boxes.indexOf(box);
boxes[k] = {
...boxes[k]};
boxes[k].x++;
this.setState({
boxes: boxes});
}
render() {
return (
<React.Fragment>
<button
onClick={
this.handleReset}
style={
{
marginBottom: '15px'}}
className='btn btn-info'
>Reset</button>
{
this.state.boxes.map(box => (
<Box key={
box.id} id={
box.id} x={
box.x} name='yyj'
onDelete={
this.handleDelete}
onClickLeft={
() => this.handleClickLeft(box)}
onClickRight={
() => this.handleClickRight(box)}
/>
))}
</React.Fragment>
);
}
}
export default Boxes;
次に、それを変更しBox
、this.state
親コンポーネントによって渡されたものと置き換えますprops
。
import React, {
Component } from 'react'; // 输入imrc即可补全
class Box extends Component {
// 输入cc即可补全
render() {
// Component类的函数,用来返回当前组件最后渲染的HTML结构是什么
return (
// HTML标签中可以使用{}写一个表达式
<React.Fragment>
<div style={
this.getStyles()}>{
this.props.x}</div>
<button onClick={
this.props.onClickLeft} className='btn btn-primary m-2'>Left</button>
<button onClick={
this.props.onClickRight} className='btn btn-success m-2'>Right</button>
<button onClick={
() => this.props.onDelete(this.props.id)} className='btn btn-danger m-2'>Delete</button>
</React.Fragment>
);
}
getStyles() {
let styles = {
width: '50px',
height: '50px',
backgroundColor: 'lightblue',
color: 'white',
textAlign: 'center',
lineHeight: '50px',
borderRadius: '5px',
position: 'relative',
left: this.props.x
};
if (this.props.x === 0) {
styles.backgroundColor = 'orange';
}
return styles;
}
}
export default Box;
5. 兄弟コンポーネント間でメッセージを渡す
コンポーネントの構造的関係がより複雑な場合、複数のコンポーネントによって共有されるデータは、最も近い共通の祖先this.state
に保存する必要があります。
App
2 つのサブコンポーネントNavBar
(ナビゲーション バー) と を含むコンポーネントを作成しますBoxes
。これら 2 つのコンポーネントは兄弟コンポーネントです。
1つ目は次のとおりですnavbar.jsx
。
import React, {
Component } from 'react';
class NavBar extends Component {
state = {
}
render() {
return (
<nav className="navbar bg-body-tertiary">
<div className="container-fluid">
<a className="navbar-brand" href="/">Navbar</a>
</div>
</nav>
);
}
}
export default NavBar;
そしてapp.jsx
、:
import React, {
Component } from 'react';
import NavBar from './navbar';
import Boxes from './boxes';
class App extends Component {
state = {
}
render() {
return (
<React.Fragment>
<div className='container'>
<NavBar />
<Boxes />
</div>
</React.Fragment>
);
}
}
export default App;
ここで、いくつかのコンポーネントに関する情報をNavBar
に保存したいとします。その場合、その情報は、これら 2 つのコンポーネントの最新の共通祖先にのみ配置できます。Boxes
Box
App
に関連するすべてのコンテンツを次の場所に移動Boxes
します。state
App
import React, {
Component } from 'react';
import Box from './box';
class Boxes extends Component {
render() {
return (
<React.Fragment>
<button
onClick={
this.props.onReset}
style={
{
marginBottom: '15px'}}
className='btn btn-info'
>Reset</button>
{
this.props.boxes.map(box => (
<Box key={
box.id} id={
box.id} x={
box.x} name='yyj'
onDelete={
this.props.onDelete}
onClickLeft={
() => this.props.onClickLeft(box)}
onClickRight={
() => this.props.onClickRight(box)}
/>
))}
</React.Fragment>
);
}
}
export default Boxes;
移動後はApp
以下の通りです。
import React, {
Component } from 'react';
import NavBar from './navbar';
import Boxes from './boxes';
class App extends Component {
state = {
boxes: [
{
id: 1, x: 0},
{
id: 2, x: 1},
{
id: 3, x: 2},
{
id: 4, x: 3},
]
}
handleDelete = (boxId) => {
// 遍历一遍state.boxes,将box.id不为传入的参数boxId的数据保留下来
const res = this.state.boxes.filter(box => box.id !== boxId);
this.setState({
boxes: res});
}
handleReset = () => {
const res = this.state.boxes.map(box => {
return {
id: box.id,
x: 0,
}
});
this.setState({
boxes: res});
}
// 需要知道修改的是哪个box
handleClickLeft = (box) => {
const boxes = [...this.state.boxes]; // 浅拷贝一份
const k = boxes.indexOf(box); // 传入的box是引用,找出其在boxes中的下标k
boxes[k] = {
...boxes[k]}; // 再clone一遍,相当于创建新的state,深拷贝
boxes[k].x--;
this.setState({
boxes: boxes});
}
handleClickRight = (box) => {
const boxes = [...this.state.boxes];
const k = boxes.indexOf(box);
boxes[k] = {
...boxes[k]};
boxes[k].x++;
this.setState({
boxes: boxes});
}
render() {
return (
<React.Fragment>
<div className='container'>
<NavBar
boxesCount={
this.state.boxes.length} // 将长度传给NavBar
/>
<Boxes
boxes={
this.state.boxes}
onReset={
this.handleReset}
onClickLeft={
this.handleClickLeft}
onClickRight={
this.handleClickRight}
onDelete={
this.handleDelete}
/>
</div>
</React.Fragment>
);
}
}
export default App;
の長さ情報を でNavBar
読み取ることができるようになりました。Boxes
import React, {
Component } from 'react';
class NavBar extends Component {
state = {
}
render() {
return (
<nav className="navbar bg-body-tertiary">
<div className="container-fluid">
<a className="navbar-brand" href="/">
Navbar <span>Boxes Count: {
this.props.boxesCount}</span>
</a>
</div>
</nav>
);
}
}
export default NavBar;
6. ステートレス機能コンポーネント
コンポーネント内で が使用されていない場合は、ステートレス関数コンポーネントthis.state
と略すことができます。関数に対するクラスの最大の利点は、状態 (ローカル変数) を簡単に維持できることです。
ステートレス機能コンポーネントなので、入力をsfc
自動で完了することができます。render
関数コンポーネントは、関数のみを備えたクラスコンポーネントと同等です。注: 関数の受信パラメータはprops
オブジェクトです。
import React from 'react';
const NavBar = (props) => {
return (
<nav className="navbar bg-body-tertiary">
<div className="container-fluid">
<a className="navbar-brand" href="/">
Navbar <span>Boxes Count: {
props.boxesCount}</span>
</a>
</div>
</nav>
);
}
export default NavBar;
7. コンポーネントのライフサイクル
Mount
サイクル (マウント、オブジェクトが作成されたことを示す)、実行シーケンス (3 つの関数を順番に実行):constructor() -> render() -> componentDidMount()
Update
サイクル (ボタンのクリックなどの変更)、実行シーケンス:render() -> componentDidUpdate()
Unmount
サイクル(削除)、実行順序:componentWillUnmount()
このうち、関数には 2 つのパラメータがあり、componentDidUpdate
それぞれ更新props
と更新前を表しますstate
。
import React, {
Component } from 'react';
import NavBar from './navbar';
import Boxes from './boxes';
class App extends Component {
state = {
boxes: [
{
id: 1, x: 0},
{
id: 2, x: 1},
{
id: 3, x: 2},
{
id: 4, x: 3},
]
}
componentDidUpdate(prevProps, prevState) {
console.log('App - Update');
console.log(prevProps, this.props);
console.log(prevState, this.state);
}
...
render() {
console.log('App - Render');
return (
...
);
}
}
export default App;
出力state
内容は以下のとおりです。
{boxes: Array(4)}
boxes: Array(4)
0: {id: 1, x: 0}
1: {id: 2, x: 1}
2: {id: 3, x: 2}
3: {id: 4, x: 3}
length: 4
[[Prototype]]: Array(0)
[[Prototype]]: Object
{boxes: Array(4)}
boxes: Array(4)
0: {id: 1, x: 1} (此处有区别)
1: {id: 2, x: 1}
2: {id: 3, x: 2}
3: {id: 4, x: 3}
length: 4
[[Prototype]]: Array(0)
[[Prototype]]: Object