AcWing
  • 首页
  • 活动
  • 题库
  • 竞赛
  • 应用
  • 更多
    • 题解
    • 分享
    • 商店
    • 问答
    • 吐槽
  • App
  • 登录/注册

Web应用课——5.2 React——组合Components(boxes-app)

作者: 作者的头像   ._675 ,  2023-05-25 17:57:29 ,  所有人可见 ,  阅读 38


0


屏幕截图 2023-05-25 175146.png
屏幕截图 2023-05-24 190522.png
屏幕截图 2023-05-24 210753.png

目录结构

屏幕截图 2023-05-25 175234.png

index.jsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import 'bootstrap/dist/css/bootstrap.css';
import App from './components/app';

//index.js是整js的入口
//将定义好的组件渲染出来

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);

box.js

import React, { Component } from 'react';
class Box extends Component {
    //如果想在外面修改当前state的值,就不能在当前组件里面存储state
    //就要在当前组件渲染外面state的值
    //当前这个组件里面所有用到state的地方,都需要修改成我们传过来的值
    // state = {  
    //     //把值传过来之后,可以将这个值更新到state里面
    //     x:this.props.x,//他就可以将x初始化进来
    //     // x:0,   
    // } 

    //将当前组件里的state删掉,如果要删掉的话,handleClickLeft和handleClickRight也会修改自己state的值
    //删完之后我们就不能修改自己state的值了,所以这两个函数也需要移到外面
    //将这两个函数剪切在boxes里
    // handleClickLeft=(step)=>{
    //     this.setState({
    //         // 只会在初始化的时候执行一次  
    //         x:this.state.x-step
    //     })

    //     console.log("click left",this);
    // }
    // handleClickRight(step){
    //     this.setState({
    //         x:this.state.x+step
    //     })

    //     console.log("click right",this);        
    // }

    //1Mount周期
    // constructor(){
    //     super();
    //     console.log("Box - Constructor");
    // }
    // //3Mount周期
    // componentDidMount(){
    //     console.log("Box - Mounted");
    // }

    //2 Update周期
    // componentDidUpdate() {
    //     console.log("Box - Updated");
    // }

    //Unmount周期
    //每一个组件可能会绑定一些事件,我们可以在删除之前做一个清理,把一些事件、数据清理掉
    componentWillUnmount(){
        console.log("Box - Unmout");
    }

    //将所有用到state的地方都变成this.props.box
    render() { 
        //2Mount周期
        // console.log("Box - Rendered");

        // 1.4.2 从上往下传递数据
        //props里存储的就是在这个组件在Boxes中定义的时候里面填写的各种属性,除了key以外
        // console.log(this.props);
        return (
        <React.Fragment>
            {/* 1.4.3 传递子节点 */}
            {/* {this.props.children[0]} */}
            <div style={this.getStyles()}>{this.toString()}</div>
            {/* {this.props.children[1]} */}
            <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>
            {/* 在box里面调用boxes的函数 */}
            <button onClick={()=>this.props.onDelete(this.props.box.id)} className='btn btn-danger m-2'>Delete</button>
        </React.Fragment> 
        );
    }
    getStyles(){

        let styles = {
            width: 50,
            height:50,
            backgroundColor: "lightblue",
            color:"white",
            textAlign:"center",
            lineHeight:"50px",
            borderRadius:"5px",
            marginLeft: this.props.box.x,
         };
         if(this.props.box.x<=0) styles.backgroundColor='orange';
         return styles;
    }

    toString() {
        const {x}=this.props.box;
        return `x:${x}`;
     }
}

export default Box;

boxes.jsx

import React, { Component } from 'react';
import Box from './box';

//在维护state值的时候,同一个数据只能存一份,如果存多份的时候就会出现有些时候我们改完之后会发现,有些地方的值变了,但是有些地方的值不变,就会发生数据不同步的情况

//把boxes变成一个函数组件sfc
//函数组件相当于是只有函数组件
//对象的解构可以在参数里面做,解构完之后就可以将props删掉了
// const Boxes = ({onReset,boxes,onDelete,onClickLeft,onClickRight}) => {
//     return (
//         <React.Fragment>
//             <button 
//             onClick={onReset}
//             style={{marginBottom:"15px"}} className='btn btn-dark'>Reset</button>
//             {boxes.map(box=>(
//                 <Box 
//                 key={box.id}
//                 box={box}
//                 onDelete={onDelete}
//                 onClickLeft={()=>onClickLeft(box)}
//                 onClickRight={()=>onClickRight(box)}
//                 />            

//             ))}
//         </React.Fragment>

//     );
// }

// export default Boxes;

class Boxes extends Component {
    //将state的所有内容放到app里面去,将定义和操作他的所有内容都放过去
    // state = {  
    //     // 把所有Box的所有信息存到state里面
    //     //boxes存一个数组,boxes里面每一个需要存一个x,另外需要存一个id,因为react组件如果有若干个儿子的话,儿子的key需要不一样
    //     //删完之后直接在box组件里面直接用boxes组件里面的state值就可以了

    //     boxes: [
    //         {id:1,x:1},
    //         {id:2,x:2},
    //         {id:3,x:3},
    //         {id:4,x:4},
    //     ]
    // } 

    // //实现在当前组件里改完x之后,能够影响到box里面的值:
    // // 可以将box里的state值直接删掉,由于box里面的state的值不能在外面修改,所以直接将他弃用(删掉)就可以了

    // handleClickLeft=(box)=>{
    //     //注意: 不要直接修改this.state的值,因为setState函数可能会将修改覆盖掉。
    //     //所以先将boxes复制一遍
    //     const boxes=[...this.state.boxes];
    //     //indexOf可以在一个数组里面找到某一个书的下标
    //     const k=boxes.indexOf((box));
    //     //将第k个boxes的值重新克隆出来,因为boxes[k]他其实存的是原数组里的第k个值
    //     //把他克隆出来可以再展开一遍,再展开一遍就相当于是把它创建了一个新的
    //     boxes[k]={...boxes[k]};
    //     boxes[k].x--;//将新的这个值得x减减
    //     this.setState({boxes});//将对于boxes的修改更新到state里面
    // }
    // handleClickRight(box){
    //     const boxes=[...this.state.boxes];
    //     const k=boxes.indexOf((box));
    //     boxes[k]={...boxes[k]};
    //     boxes[k].x++;
    //     this.setState({boxes});        
    // }

    // handleReset = () => {
    //     const boxes=this.state.boxes.map(b=>{
    //         // 点完reset之后,当前这个组件boxes里面的state里面的值确实变成0了
    //         // 但是并没有真的将dom里面的值变成0 
    //         // 因为如果向修改某一个组件值,一定只能在组件内部修改,不能在另外一个组件里修改这个组件的值
    //         // (即,不能在boxes里修改box的x值),修改的只是当前组件boxes里的state,
    //         // 但是最后在div中显示出来的x值其实应该是box组件里的state
    //         // box里面的state x:this.props.x,这句话他只会在第一次执行的时候将box里面的state的值改变成从boxes传过来的值
    //         // 但是后面每次修改的时候x:this.props.x这句话不会重复执行,他只会在初始化的时候执行一次
    //         // 所以后面不管怎么去修改x:this.props.x里面的值,x都不会发生变化了
    //         return{ 
    //             id:b.id,
    //             x:0,
    //         }
    //     });
    //     this.setState({boxes});
    //     console.log(this.state)
    //  }

    // handleDelete = (boxId) => {
    //     console.log("handle delete", boxId)
    //     const boxes=this.state.boxes.filter(b=>b.id!==boxId);
    //     //语法糖:在一个对象里如果key和value是一样的话,可以只写一个
    //     // this.setState({ boxes: boxes });
    //     this.setState({ boxes});
    //  }

    //1Mount周期
    // constructor(){
    //     super();
    //     console.log("Boxes - Constructor");
    // }
    // //3Mount周期
    // componentDidMount(){
    //     console.log("Boxes - Mounted");
    // }
    //2 Update周期
    // componentDidUpdate() {
    //     console.log("Boxes - Updated");
    // }
    render() { 
        //2Mount周期
        // console.log("Boxes - Rendered");
        return (
            // 当一个组件里面包含多个并列的元素的时候,我们需要用一个标签将他们括起来
            // 可以用react里的一个虚拟标签将他们括起来<React.Fragment>

            <React.Fragment>
                <button 
                onClick={this.props.onReset}
                style={{marginBottom:"15px"}} className='btn btn-dark'>Reset</button>
                {/* 改成用一个map来写
                <Box />
                <Box />
                <Box />
                <Box /> */}
                {this.props.boxes.map(box=>(
                    //可以将所有要传的数据的参数写在<Box />里面,可以任意起名
                    //例如向传一个x,写完x之后,就可以在Box组件里面找到这个属性了
                    <Box 
                    key={box.id}
                    // x={box.x}
                    //如果只定义了名称没定义值的话,值就是true
                    // name='xxx'
                    //也可以传一个对象 box={box}
                    // id={box.id}
                    box={box}
                    // 将函数传给box里面的onClick
                    onDelete={this.props.onDelete}

                    //为了能够在box组件内部调用组件外部的函数,需要将组件外部的函数作为属性传过去
                    onClickLeft={()=>this.props.onClickLeft(box)}
                    onClickRight={()=>this.props.onClickRight(box)}
                    />
                    //     {/* 如果在定义的组件之间加一些子元素的话,子元素的值也是可以传过来的 */}
                    //     <h1>Box:</h1>
                    //     <p>#{box.id}</p>
                    // </Box>



                ))}
            </React.Fragment>

        );
    }
}

export default Boxes;

navbar.jsx

import React, { Component } from 'react';

// 函数组件sfc
//函数组件相当于是只有一个render函数,把render函数的内容复制过来,然后将其余内容删掉
//类组件是可以通过this.props传递信息的,但是函数组件props是传在参数里面,要将所有的this都删掉
// 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;

class NavBar extends Component {
    // 类组件里如果没有局部变量的话,就可以把他变成一个无状态的函数组件
    state = {  } 

    //1Mount周期
    // constructor(){
    //     super();
    //     console.log("NavBar - Constructor");
    // }
    // //3Mount周期
    // componentDidMount(){
    //     console.log("NavBar - Mounted");
    // }

    //2 Update周期
    // componentDidUpdate() {
    //     console.log("NavBar - Updated");
    // }

    render() { 
        //2Mount周期
        // console.log("NavBar - Rendered");
        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;

// 在navbar里面存储一些boxes的信息,例如存储一下boxes里面有多少个元素,存一个count,涉及到不同组件之间传递信息,兄弟组件之间传递信息
// 在navar里调用boxes里的信息,只能把这两个节点要调用的信息放到他们的最近公共祖先App里,所以只能将boxes的state放到app里面

app.jsx

import React, { Component } from 'react';
import NavBar from './navbar';
import Boxes from './boxes';

//有两个组件navbar和boxes
class App extends Component {
    state = {  
        boxes: [
            {id:1,x:1},
            {id:2,x:2},
            {id:3,x:3},
            {id:4,x:4},
        ]
    } 

    // Mount周期
    //3.componentDidMount是说整个对象都已经被渲染出来了
    //如果想从服务器加载一些数据渲染出来,需要先将整个组件挂载完之后,再往里填数据
    //所以挂载完之后可以写一个ajax方法,从服务器加载数据
    //从服务器加载完之后,就可以通过setState函数将我们从数据库加载的信息渲染到我们当前组件里
    // componentDidMount(){
    //     // ajax();
    //     // this.setState();
    //     console.log('App - Mounted');
    // }

    // //构造函数,由于它是从某一个类里面继承过来的,那么在构造函数里第一句话一定要写super,先初始化构造函数
    // //1.先创建,Mount周期
    // constructor(){
    //     super();
    //     console.log("App - Constructor");
    // }

    //2 Update周期
    //prevProps和prevState存储我们上一个属性(即修改之前)和上一个状态
    // componentDidUpdate(prevProps,prevState) {
    //     console.log("App - Updated");
    //     // console.log("prevProps",prevProps);
    //     //输出上一个state的值和更新完之后的值
    //     console.log("prevState",prevState,this.state);

    //     //当发现状态不一样的时候,可能会更新数据库里的数据
    //     //所以一般往数据库里写数据的时候,很多时候都会写componentDidUpdate函数里面
    //     // if(prevState.boxes[0].x!==this.state.boxes[0].x){
    //     //     ajax()//更新数据库
    //     // }
    // }
    handleClickLeft=(box)=>{
        const boxes=[...this.state.boxes];
        const k=boxes.indexOf((box));
        boxes[k]={...boxes[k]};
        boxes[k].x--;
        this.setState({boxes});
    }
    handleClickRight=(box)=>{
        const boxes=[...this.state.boxes];
        const k=boxes.indexOf((box));
        boxes[k]={...boxes[k]};
        boxes[k].x++;
        this.setState({boxes});
    }
    // handleClickRight(box){
    //     const boxes=[...this.state.boxes];
    //     const k=boxes.indexOf((box));
    //     boxes[k]={...boxes[k]};
    //     boxes[k].x++;
    //     this.setState({boxes});    
    //     // console.log("click right")
    // }

    handleReset = () => {
        const boxes=this.state.boxes.map(b=>{
            return{ 
                id:b.id,
                x:0,
            }
        });
        this.setState({boxes});
     }

    handleDelete = (boxId) => {
        console.log("handle delete", boxId)
        const boxes=this.state.boxes.filter(b=>b.id!==boxId);
        this.setState({ boxes});
     }
    render() { 
        //2.渲染 Mount周期
        //在render里面会挂载新的组件,所以在render里面会递归调用每一个子组件的constructor() -> render() -> componentDidMount()
        // console.log("App - Rendered");
        return (
            <React.Fragment>
                {/* 当前boxes里的元素信息是存在了app里面,如果想在navbar里调用app里的内容,相当于是从上往下传数据,就可以通过props传数据了
                这里把长度传给navbar 
                求一下有多少个元素不为0*/}
                <NavBar boxesCount={this.state.boxes.filter(b=>b.x!==0).length} />
                {/* Boxes放到container里面,bootstrap一般都会将内容放到container里面,container是一个自适应的内容填充区域,根据屏幕的宽度来自适应调节内容区域,可以让内容区域看起来他的宽度比较合适一些 */}
                <div className='container'>
                    <Boxes 
                        boxes={this.state.boxes}
                        onReset={this.handleReset}
                        onClickLeft={this.handleClickLeft}
                        onClickRight={this.handleClickRight}
                        onDelete={this.handleDelete}
                    />
                </div>

            </React.Fragment>
        );
    }
}

export default App;

0 评论

你确定删除吗?

© 2018-2023 AcWing 版权所有  |  京ICP备17053197号-1
用户协议  |  隐私政策  |  常见问题  |  联系我们
AcWing
请输入登录信息
更多登录方式: 微信图标 qq图标 qq图标
请输入绑定的邮箱地址
请输入注册信息