Web Learning Notes-React (Combined Components)

The content of the notes is reproduced from AcWing's Web Application Course handout, course link: AcWing Web Application Course .

The content of this section is the combination of components, such as using different components to form a DOM tree, passing data to different components or calling methods of different components, and the life cycles of different components.

1. Create a parent component

BoxWe continue to work on the previous component, first create a Boxescomponent, which contains a series Boxof components.

Create in componentsdirectory boxes.jsx:

import React, {
    
     Component } from 'react';

class Boxes extends Component {
    
    
    state = {
    
      } 
    render() {
    
     
        return (
            <h1>Boxes</h1>
        );
    }
}
 
export default Boxes;

Then modify it 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 />);

Now we Boxesadd multiple elements to Box. When a component contains multiple parallel elements, we need to enclose them with a tag. You can use a virtual tag in 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;

For convenience, it can also be represented by an array, and Boxthe information of is stored in state. Since the React component has several sons, their keyneeds are different, so a unique one needs to be stored 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. Pass data from top to bottom

this.propsData can be passed from top to bottom through attributes. For example we Boxespass in 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;

BoxInformation can be output in console.log(this.props);View content:

Insert image description here

BoxUnder modification x:

import React, {
    
     Component } from 'react';  // 输入imrc即可补全

class Box extends Component {
    
      // 输入cc即可补全
    state = {
    
     
        x: this.props.x,
    };

    ...
}
 
export default Box;

3. Pass child nodes

You can write tags in <Box></Box>the form of and then add subtags to the tag:

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;

In this way, this.propsthere will be one more attribute children, and you can use []to access a sub-tag individually. We can define this passed value anywhere, for example, it can be placed at Boxthe top of each component:

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. Call functions from bottom to top

Parent elements can this.propspass information to child elements through , and child elements can also use functions to pass information to parent elements. Suppose we need to delete someone by clicking the delete button Box, and its information is stored in Boxes, statebut the event triggered by our click is in Box(note: each component's this.statecan only be modified within the component, not other components).

We can define the function in the parent element and then pass the function to the child element:

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;

In this way, the child element can call the function to operate on the parent element:

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;

Now we Boxesimplement a Reset button in to clear Boxall 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;

When observing on the console, you can find that after clicking the Reset button x, the is indeed set to zero, but Boxthe displayed xhas not changed. This is because statethe value cannot be modified externally, so we can delete Boxthe in stateand need to render the external in the component. statevalue.

Each maintained data can only be saved in one this.state. Do not modify the value of directly this.statebecause setStatethe function may overwrite the modification.

Modify Boxesand transfer the functions Boxused in the previous operations 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;

Then modify it Boxand this.statereplace it with the one passed by the parent component 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. Pass messages between sibling components

If the structural relationship of components is more complex, then data shared by multiple components needs to be stored in the nearest common ancestorthis.state .

We create a Appcomponent that contains two sub-components NavBar(navigation bar) and Boxes, these two components are sibling components.

The first is 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;

And then 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;

Now suppose we want to NavBarstore information Boxesabout several in Box, then the information can only be placed Appin the most recent common ancestor of these two components.

We will move all the content related to Boxesin to:stateApp

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;

After moving Appit is as follows:

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;

The length information of can now NavBarbe read in :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. Stateless function components

When is not used in a component this.state, it can be abbreviated as a stateless function component . The biggest advantage of classes over functions is that they can easily maintain state (local variables).

Stateless Function Component, input sfccan be automatically completed. Function components are equivalent to renderclass components with only functions. Note: The incoming parameters of the function are propsobjects:

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. Component life cycle

  • MountCycle (mounting, indicating that the object is created), execution sequence (execute three functions in sequence):constructor() -> render() -> componentDidMount()
  • UpdateCycle (modification, such as clicking a button), execution sequence:render() -> componentDidUpdate()
  • UnmountCycle (delete), execution order:componentWillUnmount()

Among them, the function has two parameters, representing the and componentDidUpdatebefore update respectively :propsstate

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;

The output statecontent is as follows:

{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

Guess you like

Origin blog.csdn.net/m0_51755720/article/details/132780159