React基础

React基础

react中创建组件可以选用JSX语法

环境要求

node > 8.1 npm > 5.6

环境搭建

npx create-react-app 名字

创建完项目之后,整理项目目录

index.js为入口文件

react文件引入

react框架可以做

  1. web端程序 — web网站
  2. app程序 — react-native框架
import React from 'react'	--- 必须的
import ReactDom from 'react - dom'	--- 

react中组件的书写模式jsx格式

jsx是JavaScript的书写格式,类似模板引擎

jsx是对JavaScript语法的扩展

说是html又不是html,说是字符串又不是字符串

jsx原理实现过程

原生:
<body>
    <div id="root">
      <button id="btn_zan">
        <span id="btn_text">点赞</span>
        <span></span>
      </button>
    </div>
  </body>
  <script>
    // 原生js实现jsx
    let btn_z = document.querySelector("#btn_zan");
    let btn_t = document.querySelector("#btn_text");
    console.log(btn_z, btn_t);
    // 原生js代码
    let isClick = false;
    btn_z.onclick = function () {
      isClick = !isClick;
      if (isClick) { 
        btn_t.innerHTML = "取消";
      } else {
        btn_t.innerHTML = "点赞";
      }
    };
  </script>
  1. 简单的处理
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    // 原生js实现jsx
    let root = document.querySelector("#root");

    // 1. 抽离一个按钮组件
    class button {
      render() {
        return `<button id="btn_zan">
        <span id="btn_text">点赞</span>
        <span>?</span>
      </button>`;
      }
    }
    // 2. 将抽取的按钮组件放回到root中
    root.innerHTML = new button().render();

    let btn_z = document.querySelector("#btn_zan");
    let btn_t = document.querySelector("#btn_text");
    console.log(btn_z, btn_t);
    // 原生js代码
    let isClick = false;
    btn_z.onclick = function () {
      isClick = !isClick;
      if (isClick) {
        btn_t.innerHTML = "取消";
      } else {
        btn_t.innerHTML = "点赞";
      }
    };
  </script>
</html>

  1. 组件化添加事件 — 事件提到组件内部书写
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <div id="root"></div>
  </body>
  <script>
    // 原生js实现jsx
    let root = document.querySelector("#root");
    // 3. 抽离一个creatElement方法
    function creatElement(str) {
      let parent = document.querySelector("#root");
      parent.innerHTML = str;
      // 返回父级
      return parent;
    }

    // 1. 抽离一个按钮组件
    class button {
      render() {
        // el就是根
        this.el = creatElement(`<button id="btn_zan">
        <span id="btn_text">点赞</span>
        <span>?</span>
      </button>`);
        // 4.添加事件
        this.el.addEventListener("click", function () {
          console.log(111);
        });
        return this.el;
      }
    }
    // 2. 将抽取的按钮组件放回到root中
    root.appendChild(new button().render());
  </script>
</html>

  1. 完善事件功能,添加状态变量

React基础语法

组件的写法

  1. 函数组件 — 无状态组件
  2. 类组件 — 有状态组件

区别:

  • 函数组件用来渲染UI界面
  • 类组件用来写复杂的逻辑代码

类组件的写法

写法一

// 声明class组件
import React from 'react'

// 组件名首字母必须大写
class IndexPage extends React.Component{
    constructor(){
        super();
    }
    render(){
        return(
        <div>组件</div>
        )
    }
} 
export default IndexPage

写法二

import { Component } from "react";

class 名字 extends Component {
  constructor(props) {
    super(props);
    this.state = {  }
  }
  render() { 
    return (  );
  }
}
 
export default 名字;

函数组件的写法

//创建纯函数组件

function My({dataindex}){
    console.log(dataindex);
    return (
        <div>
            我的组件
        </div>
    )
}
// function My(props){
//     console.log(props);
//     return (
//         <div>
//             我的组件
//         </div>
//     )
// }
export default My;

组件的挂载

import './App.css';
//引入组件--同步引入挂载
import IndexPage from '../view/Index-page'
import MyPage from '../view/My-page'
import Cart from '../view/Cart'

function App() {
  return (
    <div className="App">
        默认主模板
        {/*挂载的标签使用方式
        <html></html>
        <html/>
        */}
        <IndexPage name='张三'></IndexPage>
        <MyPage dataindex="123"/>
        <Cart></Cart>
    </div>
  );
}

export default App;

组件中的super关键字

如果在声明中没有使用super会报以下错误

ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor

super() 是执行父类的构造

类组件中

 constructor(props) {
      console.log("props是:", props); // props是: {}
        super();//在使用this之前一定super()
        //类组件中的状态值
        this.state={
            msg:'xiaoxi'
        }
    }
    play() {
    console.log(this.state, this.props);	// {num: 0} {}
    }

类组件中的state是状态值,props是接收组件传值 — 属性配置

start与props的区别?

  1. start是属性的状态值,props是组件的属性配置–传值
  2. 函数组件中的props与类组件中的props作用相同
  3. state是可以修改的,props是只读的

props按照解构的写法书写(没理解)

函数props解构
function My({dataindex}){
    console.log(dataindex);
    return (
        <div>
            我的组件
        </div>
    )
}
类中写法
 constructor({name}) {
        super();//在使用this之前一定super()
        //类组件中的状态值
        this.state={
            msg:'xiaoxi'
        }
    }

类组件中状态值的使用

 render() {
        //直接解构
        let { list } = this.state;
        return (
            <div>
                购物车
                {/* 绑定变量 */}
                {this.state.num}
                {this.state.str}
                {/* {}内部可以书写三元运算符 */}
                {this.state.ishas ? '取消' : '点赞'}
                {this.state.ishas ? <h2>取消</h2> : <h1>点赞</h1>}
                {/* 数组类型变量 */}
                {
                    this.state.list
                }
                {
                    list.map((item,index)=>{
                        return (
                            <h1>
                                {item}
                            </h1>
                        )
                    })
                }
            </div>
        );
    }
    循环渲染报元素key
    
      {
           list.map((item,index)=>{
                        return (
                            <h1 key={index}>
                                {item}
                            </h1>
                        )
                    })
      }

类组件中定义细节

 this.state = {
            num: 0,
            str: 'abc',
            ishas: false,
            list: [1, 2, 3, 4],
            stu: [
                {
                    name: '小花',
                    hobby: ['篮球']
                },
                {
                    name: '小米',
                    hobby: ['篮球', '乒乓球']
                }
            ],
            un: <h1>测试</h1>
        }
绑定
render() {
        //直接解构
        let { list, stu } = this.state;
        return (
            <div>
                购物车
                {/* 绑定变量 */}
                {this.state.num}
                {this.state.str}
                {/* {}内部可以书写三元运算符 */}
                {this.state.ishas ? '取消' : '点赞'}
                {this.state.ishas ? <h2>取消</h2> : <h1>点赞</h1>}
                {/* 数组类型变量 */}
                {
                    this.state.list
                }
                {
                    list.map((item, index) => {
                        return (
                            <h1 key={index}>
                                {item}
                            </h1>
                        )
                    })
                }
                {
                    stu.map((item, index) => {
                        return (
                            <div key={index}>
                                {item.name}
                                ----
                                {
                                    item.hobby.map((it, i) => {
                                        return <span key={i}>{it}</span>
                                    })
                                }
                            </div>
                        )
                    })
                }
                {
                    this.state.un
                }
            </div>
        );
    }

标签上的属性绑定

  {/* 标签上的属性绑定
                
                分为静态和动态绑定*/}
                <button title="随便">按钮</button>
                <button title={this.state.str}>按钮</button>
                
    {/* 元素类绑定 class 
                    class不能直接使用
                    class----className
                    for   htmlFor
                */}

                {/* <div className='cart'></div> */}
                <div className={CartCss.cart}></div>
                <div style={this.state.style}></div>
                <div style={obj}></div>
                <div className={this.state.ishas?'':''}></div>
     如果是按照模块方式加载css文件
     文件名字命名:
import { Component } from "react";
// 方法一:直接引入
// import "../assets/css/index.module.css";
// 方法二:声明引入
import cl from "../assets/css/xx.module.css";

// 方法四:直接定义css
let obj = {
  color: "yellow",
};
class name extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "王五",
      // 方法三:直接在state中声明,绑定成行内样式
      sty: {
        color: "red",
      },
    };
  }
  render() {
    return (
      <div>
        {/* 静态绑定 */}
        <input type="text" value="张三" />
        {/* 动态绑定 */}
        <input type="text" value={this.state.name} />

        {/* 方法一:直接引入 */}
        {/* <input type="text" value="张三" className="bbb" /> */}
        {/* 方法二:声明引入 */}
        <input type="text" value="张三" className={cl.bbb} />
        {/* 方法三:直接在state中声明,绑定成行内样式 */}
        <input type="text" value="张三" style={this.state.sty} />
        {/* 方法四:直接定义css */}
        <input type="text" value="张三" style={obj} />
      </div>
    );
  }
}

export default name;

修改state

使用API setState()

在reactjs库管控区域属于异步,非管控位置属于同步

setState方法在修改之后 直接获取无法获取最新的值—setState是异步的想要获取更新之后的数据 setState 它的第二个参数为一个匿名函数回调执行修改操作之后返回最新值
setState第二个参数为啥是一个函数

对象写法

//参数使用对象格式
        this.setState({
            msg:'你好'
        });
        console.log(this.state.msg);//输出的不是修改
        //确定是异步

对象类型写法

import { Component } from "react";

class 名字 extends Component {
  constructor(props) {
    super(props);
    this.state = { num: 0 };
  }
  play() {
    this.setState(
      {
        num: this.state.num + 1,
      },
      () => {
        console.log(this.state.num);
      }
    );
    console.log(this.state.num);
  }
  render() {
    return (
      <div>
        {this.state.num}
        <button onClick={this.play.bind(this)}>点击--{this.state.num}</button>
      </div>
    );
  }
}

export default 名字;
import { Component } from "react";

class 名字 extends Component {
  constructor(props) {
    super(props);
    this.state = { num: 0 };
  }
  play() {
    this.setState(
      {
        num: this.state.num + 1,
      },
      () => {
        console.log(this.state.num); // 1
      }
    );
    // setState是异步,实质上是与原数据进行比较替换,
    // 故两次this.state.num + 1均传了1回去
    this.setState(
      {
        num: this.state.num + 1,
      },
      () => {
        console.log(this.state.num); // 1
      }
    );
    console.log(this.state.num); // 0
  }
  render() {
    return (
      <div>
        {this.state.num}
        <button onClick={this.play.bind(this)}>点击--{this.state.num}</button>
      </div>
    );
  }
}

export default 名字;

参数写成函数类型写法

import { Component } from "react";

class 名字 extends Component {
  constructor(props) {
    super(props);
    this.state = { num: 0 };
  }
  play() {
      // 使用函数的写法,就会使用另一个语句,函数会排队故num: xxx.num + 1,可以累计
      this.setState(
        (xxx) => {
          console.log(xxx); // {num:0}
          return {
            num: xxx.num + 1,
          };
        },
        () => {
          console.log(this.state.num);	// 1
        }
      );
      this.setState(
        (xxx) => {
          console.log(xxx); // {num:0}
          return {
            num: xxx.num + 1,
          };
        },
        () => {
          console.log(this.state.num);	// 2
        }
      );
  }
  render() {
    return (
      <div>
        {this.state.num}
        <button onClick={this.play.bind(this)}>点击--{this.state.num}</button>
      </div>
    );
  }
}

export default 名字;

非管控位置指的就是被js原生组件包括的区域

import { Component } from "react";

class 名字 extends Component {
  constructor(props) {
    super(props);
    this.state = { num: 0 };
  }
  play() {
    setTimeout(() => {
      this.setState({
        num: 3,
      });
      // setState被转化为同步函数,故:输出值为3 而不是0
      console.log(this.state.num); // 3
    });
  }
  render() {
    return (
      <div>
        {this.state.num}
        <button onClick={this.play.bind(this)}>点击--{this.state.num}</button>
      </div>
    );
  }
}

export default 名字;

props验证

import { Component } from "react";
//开始props验证
import Propstypes from "prop-types"; // 所有的数据类新

//props属性约束是类的静态字段  (类名称点)

//函数组件外部可以通过函数名称点类型约束
//函数内部不能写  static

class 名字 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      text: "props验证",
    };
  }

  // 方法二:内部定义(static:静态字段关键字)
  static propTypes = {
    name: Propstypes.string,
  };
  render() {
    console.log(this.props);
    return <div>{this.state.text}</div>;
  }
}

// 第一种是在类或者函数外部直接定义,类型约束(注意:propTypes指的是propTypes对象、类的静态字段)
名字.propTypes = {
  name: Propstypes.string,
};
// 定义props的默认值
名字.defaultProps = {
  name: "你叫啥名字?",
};

export default 名字;

事件代理

import { Component } from "react";

class 名字 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      top: this.addHandle.bind(this),
    };
  }

  // 方案一
  handle() {
    console.log(this);
  }
  // 方案二
  head = () => {
    console.log(this);
  };
  // 方案三
  addHandle() {
    console.log(this);
  }
  render() {
    return (
      <div>
        {/* 方案一 */}
        <button onClick={this.handle.bind(this)}>按钮</button>

        {/* 方案二 */}
        <button onClick={this.head}>按钮</button>

        {/* 方案三 */}
        <button onClick={this.state.top}>按钮</button>
      </div>
    );
  }
}

export default 名字;

事件的执行参数

常用属性

 addHandle(e){
        //事件参数中常用属性
        //坐标 PageX  PageY ClientX  ClientY
        //触发的目标元素  e.target
        //e.preventDefault()  阻止事件的默认行为
        //e.stopPropagation() 阻止事件冒泡的
        console.log(e.target);//可以直接操作原生js
        console.log(this,e)
    }

事件执行的时候传递参数

几种方案

								//事件绑定传值  事件参数后移
                sendMsg(args,args1,e) {
                    console.log('执行', args,args1,e,arguments);
                }
                
                
								{/* 事件传参 */}
                {/* <button onClick={() => {
                    //基本的事件执行  调用方法传参
                    this.sendMsg(1);
                }}>传参</button> */}

                {/* <button onClick={this.sendMsg.bind(this,1,2)}>传参</button> */}
                <button onClick={this.sendMsg}>传参</button>

react中的元素事件不是原生的js事件 类似事件 是元素的属性

理解执行机制:

react中存在单独的事件处理机制,类似委托,所有的react元素的类事件属性全部注册到document上通过addEventListener,存储所有的事件执行函数,在挂载完成阶段委托到当前元素 执行对应的事件处理函数。

先注册到document实现捕获,在到目标阶段,在找到当前触发元素,根据key去对应事键存储中找到对应的事件处理函数,在进行合成(把目标和事件处理函数合成到events对象),在进行批处理(执行)。

image-20211117170450497

受控与非受控组件

受控组件

有React管理数据的组件叫受控组件

react推荐使用受控组件来书写表单数据

react中的数据是单项数据流*****

表单元素的value 不是原生js的value属性, defaultValue是原生js的value

受控组件中的value需要借助onChange事件来实现数据双向

受控组件自己的state状态需要由自己维护需要onChange事件实现双向

 setName(e) {
        //实现数据双向
        let { target } = e;
        let { user } = this.state;
        user.name = target.value;//获取原生js的value属性值
        this.setState({
            user: user
        });
    }
    render() {
        let { user } = this.state;
        return (
            <div>
                <form>
                    <ul>
                        <li>
                            <label htmlFor="userName">姓名:</label>
                            <input name='userName' onChange={this.setName.bind(this)} value={user.name} type="text" />
                        </li>
                        <li><button>提交</button></li>
                    </ul>
                </form>
            </div>
        );
    }

受控组件的使用

各种表单元素

最简单的表单
import React, { Component } from "react";

class 名字 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "",
    };
    this.handle = this.handle.bind(this);
  }

  handle(e) {
    let target = e.target;
    console.log(target);	// 元素本身 <input name="name" type="text" value="a">
    this.setState({
      name: target.value,
    });
  }

  render() {
    let { name } = this.state;
    return (
      <div>
            <label htmlFor="name">姓名</label>
            <input
                name="name"
                type="text"
                value={name}
                onChange={this.handle}
                />
      </div>
    );
  }
}

export default 名字;

import React, { Component } from 'react';
class Index extends Component {
    constructor(props) {
        super(props);
        this.state = {
            // user: {
            //     name: '',
            //     age: 0
            // }
            name: '',
            age: 0,
            sex: '男',
            hobby: [
                {
                    name: '篮球',
                    isck: false
                },
                {
                    name: '足球',
                    isck: false
                }
            ],
            city: '',
            txt: ''
        }
        this.handle = this.handle.bind(this);
    }
    setName(e) {
        //实现数据双向
        let { target } = e;
        let { user } = this.state;
        user.name = target.value;//获取原生js的value属性值
        this.setState({
            user: user,
        });
    }
    setAge(e) {
        //实现数据双向
        let { target } = e;
        let { user } = this.state;
        user.age = target.value;//获取原生js的value属性值
        this.setState({
            user: user,
        });
    }
    //优化使用字面量解析实现优化
    handle(e) {
        //获取目标元素
        let target = e.target;
        let name = target.name;//获取元素的名称  ---对应的就是变化的属性
        switch (name) {
            case 'hobby':
                //获取对应的索引
                let index = parseInt(target.getAttribute('data-index'));
                let { hobby } = this.state;
                hobby[index].isck = !hobby[index].isck;
                this.setState({
                    hobby: hobby
                });
                break;
            default:
                this.setState({
                    [name]: target.value
                });
        }
    }
    //表单提交方法
    subInfo(e) {
        console.log(this.state);
        //阻止事件默认行为
        e.preventDefault();
    }
    render() {
        let { name, age, sex, hobby, city, txt } = this.state;
        return (
            <div>
                <form onSubmit={this.subInfo.bind(this)}>
                    <ul>
                        <li>
                            <label htmlFor="name">姓名:</label>
                            <input name='name' onChange={this.handle} value={name} type="text" />
                        </li>
                        <li>
                            <label htmlFor="age">年龄:</label>
                            <input name='age' onChange={this.handle} value={age} type="text" />
                        </li>
                        <li>
                            <label htmlFor="sex">性别:</label>
                            <input type="radio" checked={sex === '男'} value='男' onChange={this.handle} name='sex' />男
                            <input type="radio" checked={sex === '女'} value='女' onChange={this.handle} name='sex' />女
                        </li>
                        <li>
                            <label htmlFor="hobby">爱好:</label>
                            {
                                hobby.map((item, index) => {
                                    return (
                                        <div key={index}>
                                            <input name='hobby' checked={item.isck} data-index={index} onChange={this.handle} type="checkbox" value={item.name} />{item.name}
                                        </div>
                                    )
                                })
                            }
                        </li>
                        <li>
                            <label htmlFor="city">城市:</label>
                            <select name='city' value={city} onChange={this.handle}>
                                <option value=''>--请选择--</option>
                                <option value='西安市'>--西安市--</option>
                                <option value='宝鸡市'>--宝鸡市--</option>
                                <option value='咸阳市'>--咸阳市--</option>
                            </select>
                        </li>
                        <li>
                            <label htmlFor="txt">备注:</label>
                            <textarea name='txt' onChange={this.handle} value={txt}></textarea>
                        </li>
                        <li><button>提交</button></li>
                    </ul>
                </form>
            </div>
        );
    }
}
export default Index;

非受控组件

  1. 类似使用原生js的代码操作数据,

    表单元素的数据不维护

    可以使用defaultValue绑定初始化

<form>
    <ul>
        <li>
            {/* defaultValue  是原生js的value属性
                                value 是受控的
                            */}
            <input type="text" onChange={this.handle.bind(this)} defaultValue={this.state.name} />
        </li>
    </ul>
</form>

还可以操作获取DOM元素节点获取值

react16.8版本之前 ref属性可以直接写在元素之上

<li>      <input type="text" ref="ipt" />//16.8 之前    </li>

现在react版本是17.0.2

import React, { Component, createRef } from "react";
// 引入函数子组件
import Son from "../component/Son";
// 引入类子组件
import Son1 from "../component/Son1";

class 非受控 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "",
    };
    this.aaa = createRef();
    this.bbb = createRef();
    this.ccc = createRef();
  }
  handel(e) {
    let target = e.target;
    console.log(target.value);
  }
  subInfo(e) {
    console.log(this.aaa);
    console.log(this.bbb);
    console.log(this.ccc);
    e.preventDefault(); // 阻止默认操作
  }
  render() {
    return (
      <div>
        {/* onsubmit 事件在表单提交时触发。 */}
        <form action="" onSubmit={this.subInfo.bind(this)}>
          <ul>
            <li>
              {/* 方法一 */}
              <input
                type="text"
                onChange={this.handel.bind(this)}
                defaultValue={this.state.name}
              />
            </li>
            <li>
              {/* 方法二:使用ref */}
              <input type="text" ref={this.aaa} />
            </li>
            <li>
              <button>确定</button>
            </li>
          </ul>
        </form>
        {/* 子组件 */}
        {/* 方法三:获得子组件内容 */}
        <Son ref={this.bbb}></Son>
        <Son1 ref={this.ccc}></Son1>
      </div>
    );
  }
}

export default 非受控;

refApi

  1. ref允许创建节点或者在render方法中创建react元素

    ref用法:

    1. ref关联到html元素标签上时,current属性是原生的js对象,可以直接操作
    2. ref关联到子组件上(class组件),current属性代表当前组件的实例
    3. 在函数组件上不能直接使用ref获取,,,函数组件没有实例化

    上面第二个方法的代码

    创建接收子组件的实例对象

import React, { Component, createRef } from "react";

// 引入函数子组件
import Son from "../component/Son";
// 引入类子组件
import Son1 from "../component/Son1";

class 非受控 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "",
    };
    this.aaa = createRef();
    this.bbb = createRef();
    this.ccc = createRef();
  }
  handel(e) {
    let target = e.target;
    console.log(target.value);
  }
  subInfo(e) {
    console.log(this.aaa);
    console.log(this.bbb);
    console.log(this.ccc);
    e.preventDefault(); // 阻止默认操作
  }
  render() {
    return (
      <div>
        {/* onsubmit 事件在表单提交时触发。 */}
        <form action="" onSubmit={this.subInfo.bind(this)}>
          <ul>
            <li>
              {/* 方法一 */}
              <input
                type="text"
                onChange={this.handel.bind(this)}
                defaultValue={this.state.name}
              />
            </li>
            <li>
              {/* 方法二:使用ref */}
              <input type="text" ref={this.aaa} />
            </li>
            <li>
              <button>确定</button>
            </li>
          </ul>
        </form>
        {/* 子组件 */}
        {/* 方法三:获得子组件内容 */}
        <Son ref={this.bbb}></Son>
        <Son1 ref={this.ccc}></Son1>
      </div>
    );
  }
}

export default 非受控;

// 函数组件
import { forwardRef } from "react";

let ReaderTable = forwardRef((props, ref) => {
  return <div ref={ref}>子组件 -- 函数组件</div>;
});

export default ReaderTable;



// 类组件
import React, { Component } from "react";
class Dialog extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: "李四",
    };
  }
  getData() {}
  render() {
    let { name } = this.state;
    return <div>子组件--{name}</div>;
  }
}

export default Dialog;

react组件间通信

组件上的props进行通信,props是只读的

父组件传递数据到子组件

父组件想要获取到整个子组件可以使用ref创建DOM节点

(class类组件可以直接获取 ref节点对象current属性直接当前组件)

(函数组件 不能直接使用ref 可以通过forwardRef转发ref到子组件的元素上)

父组件直接获取函数子组件

  1. 父 => 子

    // 父元素
    import React, { Component } from "react";
    
    // 引入子组件
    import Son1 from "../components/son1";
    
    class 首页 extends Component {
      constructor(props) {
        super(props);
        this.state = {
          // 步骤一
          text: [1, 2, 3, 4],
        };
      }
      sendMSG() {
        alert("111");
      }
      render() {
        let { text } = this.state;
        return (
          <div>
            我是首页
            <br />
            {/* 挂载子组件 */}
            {/* 
              步骤二
              下面的parent属性传递的是整个父组件
              相当于在子组件中通过parent获得整个父组件
             */}
            <Son1 parent={this} son_text={text}></Son1>
          </div>
        );
      }
    }
    
    export default 首页;
    
    
    
    
    // 子元素
    // 函数组件
    
    // 引入数据验证模块
    import propTypes from "prop-types";
    
    // props是组件传参集合,只读的
    // 步骤三
    function name({ son_text, parent }) {
      // 获得整个父组件
      console.log(parent);
      return (
        <div>
          我是你的子组件-1 <br />
          {/* 步骤四 */}
          {son_text.map((item, index) => {
            return <p key={index}>父元素传来了:{item}</p>;
          })}
          {/* 子组件执行父组件方法 */}
          <button onClick={parent.sendMSG}>执行父组件方法</button>
        </div>
      );
    }
    
    // 函数组件的约束只能在外部书写
    // 类型的约束
    name.propTypes = {
      son_text: propTypes.array,
      parent: propTypes.object,
    };
    // 默认值的约束
    name.defaultProps = {
      son_text: [],
    };
    
    export default name;
    
    
    1. 子传父
    // 父组件
    // 步骤一:导出creatRef
    import React, { Component, createRef } from "react";
    
    // 引入子组件
    // import Son1 from "../components/son1";
    import Son2 from "../components/son2";
    
    class 首页 extends Component {
      constructor(props) {
        super(props);
        this.state = {
          text: [1, 2, 3, 4],
        };
        // 步骤二:定义DOM节点对象
        this.newDOM = createRef();
      }
      // sendMSG() {
      //   alert("111");
      // }
      play() {
        console.log(this.newDOM);
      }
      render() {
        // let { text } = this.state;
        return (
          <div>
            我是首页
            <br />
            {/* 挂载子组件 */}
            {/* 
              下面的parent属性传递的是整个父组件
              相当于在子组件中通过parent获得整个父组件
             */}
            {/* 步骤三:父组件获得直接获取子组件 */}
            {/* <Son1 ref={this.newDOM} parent={this} son_text={text}></Son1> */}
            <Son2 ref={this.newDOM}></Son2>
            <button onClick={this.play.bind(this)}>获取子组件</button>
          </div>
        );
      }
    }
    
    export default 首页;
    
    
    // 子组件
    // 步骤四
    import { forwardRef } from "react";
    
    let name = forwardRef((props, ref) => {
      return (
        <div ref={ref}>
          我是你的子组件-2 <br />
        </div>
      );
    });
    
    export default name;
    
    
    
    // 类组件
    
    import React, { Component, ref } from "react";
    
    class 子页 extends Component {
      constructor(props) {
        super(props);
        this.state = {};
      }
      play2() {
        console.log("我在子组件上");
      }
      render() {
        let { my_text } = this.props;
        return (
          <div ref={ref}>
            子组件-3 <br />
            <ul>
              {my_text.map((item, index) => {
                return <li key={index}>我是子组件-3的:{item}</li>;
              })}
            </ul>
          </div>
        );
      }
    }
    
    export default 子页;
    
    

组件的状态提升

假设一个父组件中存在两个子组件,一个子组件数据发生变化 ,另一个子组件同步修改,直接使用父子传值 props是只读的不能实现,

将子组件的状态统一提升到父组件中管理

// 父组件
// 组件的状态提升
import React, { Component } from "react";

import Child1 from "../components/child1";
import Child2 from "../components/child2";

class 我的 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      num: 20,
    };
  }

  my_update(n) {
    console.log(n);
    this.setState({
      num: n,
    });
  }
  render() {
    return (
      <div>
        我的组件 <hr />
        <Child1 onUpdate={this.my_update.bind(this)}></Child1>
        <hr />
        <Child2 my_num={this.state.num}></Child2>
      </div>
    );
  }
}

export default 我的;

// 子组件1
import React, { Component } from "react";

class 子组件1 extends Component {
  constructor(props) {
    super(props);
    this.state = {
      num: 0,
    };
  }
  play(e) {
    let target = e.target;
    let name = target.name;
    this.setState({
      [name]: target.value,
    });

    this.props.onUpdate(target.value);
  }
  render() {
    return (
      <div>
        子组件1 <br />
        {this.state.num} <br />
        <input
          name="num"
          type="number"
          value={this.state.num}
          onChange={this.play.bind(this)}
        />
      </div>
    );
  }
}

export default 子组件1;

import React, { Component } from "react";

class 子组件2 extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }
  render() {
    let { my_num } = this.props;
    console.log(my_num);
    if (my_num < 0) {
      return <div>结冰了</div>;
    } else if (my_num >= 100) {
      return <div>水沸腾了</div>;
    } else {
      return <div>烧水中</div>;
    }
  }
}

export default 子组件2;

非父子组件之间传值

  1. 事件总线
  2. redux状态管理

事件总线

// 创建str/utils.event.js文件配置事件总线
import events from 'events'

let eventEmitter = new events.EventEmitter()

export default eventEmitter
// 监听事件
import React, { Component } from "react";
import Event from "../utils/event";

class 子组件2 extends Component {
    constructor(props) {
        super(props);
        this.state = {};
    }
    render() {
        let my_num = 20
        console.log(my_num);
        let But = (
            <button
                onClick={() => {
                    // 触发自定义事件
                    Event.emit("sendInfo", my_num);
                }}>弹框</button>
        );
        return (
            <div>
                结冰了 <br />
                {But}
            </div>
        );
    }
}

export default 子组件2;

// 获得监听事件传出的值
import React, { Component } from "react";
import Event from "../utils/event";

class 获取事件总线 extends Component {
  constructor(props) {
    super(props);
    this.state = {};
  }

  render() {
    Event.on("sendInfo", (params) => {
      console.log(params);
    });
    return <div>获取事件总线</div>;
  }
}

export default 获取事件总线;

react中的条件渲染

react中类似javascript中一样可以根据三元运算符和if条件动态渲染UI界面

在类组件中render进行判断(if 或者 ?:)

render() {
    //直接写if或者三元控制渲染
    let { isshow } = this.props;
    if(isshow)
    {
        return (
            <div>home另一个组件</div>
        )
    }
    return (
        <div>
            home组件
        </div>
    );
}

定义元素变量的写法控制UI

render() {
    //定义一个元素变量组件
    let { isshow } = this.state;
    let element ;
    if(isshow){
        element=<div>第一种情况</div>;
    }
    else{
        element=<div>第二种情况</div>;
    }
    return (
        <div>
            home组件
            {element}
        </div>
    )
}

条件+&&符操作UI

<div>
    home组件
    {element}
    {this.state.arr.length > 0 && <h2>h2组件</h2>}
</div>

阻止react组件渲染

直接return null

react中组件的懒加载

封装一个组件懒加载的方法

//实现组件的懒加载
import React, { Component } from 'react';
//fn  接收  ()=>import('../view/Cart')
function AsyncComponent(fn) {
    return class Async extends Component {
        constructor(props) {
            super(props);
            this.state = {
                Component: null
            }
        }
        componentDidMount() {
            //执行import()  返回的是promise
            fn().then((res) => {
                this.setState({ Component: res.default });
            });
        }
        render() {
            let { Component } = this.state;
            return (
                //{...this.props} props属性传递
                Component ? <Component {...this.props}></Component> : null
            );
        }
    }
}
export default AsyncComponent;

组件中使用

//引入懒加载模块
import AsyncCom from '../util/AsyncComponent'
let Cart = AsyncCom(() => import('../view/Cart'));

安装

cnpm i --save-dev react-loadable

使用

//引入第三方懒加载
import LoadAble from 'react-loadable'
function loading() {
  return (
    <div>正在加载...</div>
  )
}
let Cart = LoadAble({
  loader: () => import('../view/Cart'),
  loading: loading
});

React的生命周期

一个组件从初始化==> 挂载完成 ==> 更新 ==> 卸载 这个过程叫组件的生命周期

组件首次初始化过程

class类组件首次挂载 执行的过程

代码:

import React, { Component } from 'react';
//class组件实例  初始化构造函数
class Index extends Component {
    constructor(props) {
        super(props);
        this.state = {}
        console.log('进入构造函数');
    }
    //组件挂载完成的生命周期
    componentDidMount() {
        console.log('组件挂载完成');
    }
    render() {
        console.log('进入render');
        return (
            <div>
                首页class组件
            </div>
        );
    }
}
export default Index;

组件首次初始化执行constructor构造 ,再执行render(编译jsx格式为虚拟DOM,在转化为真实DOM,外加事件合成机制,再去root节点挂载),最后执行挂载完成的生命周期。

真实机制constructor调用之后换回执行一个方法获取默认的props参数,getDerivedStateFromProps():

组件更新过程

// 子组件
import React, { Component } from "react";

class 生命周期 extends Component {
    constructor(props) {
        super(props);
        this.state = {
            msg: "hello",
        };
        console.log("进入构造函数");
    }
    // 挂载之前
    componentWillMount() {
        console.log("挂载之前");
    }
    // 挂载完成
    componentDidMount() {
        console.log("挂载完成");
    }
    // 组件的props发生变化
    componentWillReceiveProps(props) {
        console.log("组件的props发生变化", { props });
    }
    // 组件发生变化前
    shouldComponentUpdate(props, state) {
        console.log(props, state);
        if (state.msg !== this.state.msg || props.title !== this.props.title) {
            return true;
        }
        return false;
    }
    // 组件发生变化完成
    componentDidUpdate(props, state) {
        console.log(props, state);
    }
    componentWillUnmount() {
        console.log("卸载之前");
    }
    //事件方法
    setData() {
        this.setState({ msg: "你好" });
    }
    render() {
        console.log("进入render");
        return (
            <div>
                <p>{this.state.msg}</p>
                <button onClick={this.setData.bind(this)}>更新数据</button>
            </div>
        );
    }
}

export default 生命周期;

// 父组件
import './App.css';
import React, { Component } from 'react';
//引入组件--同步引入挂载
import 生命周期 from '../view/item'

class APP extends Component {
  constructor(props) {
    super(props);
    this.state = {
      name: '李四',
      show: true
    }
  }
  play() {
    this.setState({
      name: '张三'
    })
  }
  gogo() {
    this.setState({
      show: !this.state.show
    })
  }
  render() {
    return (<div>

      {/*挂载的标签使用方式
        <html></html>
        <html/>
        */}
      {
        this.state.show ? <生命周期 /> : null
      }
      <button onClick={this.play.bind(this)}>改变父组件的props</button>
      <button onClick={this.gogo.bind(this)}>卸载子组件</button>
    </div>);
  }
}

export default APP;
getDefaultProps得到默认的props
getinitialState得到最初的state
componentWillMount挂载之前
render渲染
componentDidMount挂载完成
componentWillReceivePropsprops发生变化之前
shouldComponentUpdate组件将要发生变化
componentDidUpdate组件发生变化
compontWillUnmount组件卸载前

组件的动态挂载

使用bool变量实现

  {/* 下面方案可以实现组件的动态挂载 */}
        {this.state.show ? <Index title={msg}></Index> : null}

数组方式

{/* 可以使用数组有值或者没值来确认组件动态挂载 */}
{
    this.state.arr.map(() => {
        return (
            <Index title={msg}></Index>
        )
    })
}

常规写的周期存在警告 旧名字可以使用 ,也可以加前缀 UNSAFE_

react中的路由

github中文文档

https://v5.reactrouter.com/web/example/basic

根据文档操作

1.安装react-router-dom

cnpm i --save-dev react-router-dom安装版本 @5.3.0   默认最新的

直接在组件中使用

import { BrowserRouter as Router, Switch, Route, Link, Redirect } from 'react-router-dom'
//引入路由需要的API
//web端的路由react-router-dom
//移动端  react-router-native
//react-router
//BrowserRouter  是浏览器的历史记录模式
//HashRouter  hash  模式网页上的锚标记导航
//Switch  默认匹配第一个子集  写Route时可以倒着写
//Route 路由配置组件

Route组件的各种写法

{/* <Route path='/Index/admin' component={Admin}></Route>
                    <Route path='/Index/cart' component={Cart}></Route> */}
{/* 下面这个写法直接把路由组件直接添加到route子集 */}
{/* <Route path='/Index/admin'>
                        <Admin></Admin>
                    </Route>
                    <Route path='/Index/cart'>
                        <Cart></Cart>
                    </Route> */}
{/* 可以在route组件上添加render  方法  执行返回路由组件 */}
<Route path='/Index/admin' render={(props) => {
        console.log(props);
        return <Admin></Admin>
    }}></Route>
<Route path='/Index/cart' render={(props) => {
        return <Cart></Cart>
    }}></Route>

Link,NavLink

<Link to='/Index/admin'>admin界面</Link>
{/* activeClassName  激活添加类 */}
<NavLink to='/Index/cart' activeClassName="active">cart界面</NavLink>

Redirect 重定向

<Redirect from='/Index' to='/Index/admin'></Redirect>

from可以不写,to必写。

一级路由代码

{/* 类似vue中的router-view */}
<Router>
    {/* 配置路由入口 */}
    <Link to="/Index">首页</Link>
    <Link to="/my">我的</Link>
    {/* exact 属性是bool类型  按照路由的严格模式匹配路由
                ----等一会测试  
                */}
    {/* Switch API  默认渲染的是第一个子集
                    默认的是  /路由
                    /路由后置
                    如果添加Switch  不需要属性exact
                */}
    {/* <Route exact path='/my' component={My}></Route>
                <Route path='/Index' component={Index}></Route> */}
    {/* 重定向 
                必写to  from可以省略
                */}
    <Switch>
        <Route path='/my' component={My}></Route>
        <Route exact path='/Index' component={Index}></Route>
        <Redirect from='/' to='/Index'></Redirect>
    </Switch>
</Router>

React中的路由封装

类似vue的路由模块化

routerView文件

//创建一个类组件
import React, { Component } from 'react';
//引入路由的组件
import { Route, Switch, Redirect } from 'react-router-dom'
class Router_module extends Component {
    constructor(props) {
        super(props);
        this.state = {}
    }
    render() {
        //获取路由的配置
        let { Routes } = this.props;
        return (
            <Switch>
                {/* 配置路由节点 */}
                {
                    // route  路由的配置信息
                    Routes.map((route, index) => {
                        return (
                            <Route exact key={index} path={route.path} render={(nowRoute) => {
                                    return <route.component {...nowRoute} Routes={route.children}></route.component>
                                    //检测当前路由是否存在二级路由
                                    // if (route.children && route.children.length) {
                                    //     return (
                                    //         // 路由组件属性向下传递
                                    //         //组件传值
                                    //         <route.component {...nowRoute} Routes={route.children}></route.component>
                                    //     )
                                    // }
                                    // else {
                                    //     return (
                                    //         <route.component {...nowRoute}></route.component>
                                    //     )
                                    // }
                                }}></Route>
                        )
                    })
                }
                <Redirect exact from='/my' to='/my/admin'></Redirect>
                <Redirect exact from='/error' to='/'></Redirect>
            </Switch>
        );
    }
}
export default Router_module;

路由的配置文件

// 路由的详细配置
import Index from '../view/Index'
import My from '../view/My'
//二级路由组件
import Admin from '../view/children/Admin'
import Cart from '../view/children/Cart'
let routes = [
    {
        path: '/',
        component: Index
    },
    {
        path: '/my',
        component: My,
        children: [
            {
                path: '/my/admin',
                component: Admin,
                name:'admin'
            },
            {
                path: '/my/cart',
                component: Cart,
                name:'cart'
            }
        ]
    }
]
export default routes;

路由在主模板使用

import { BrowserRouter as Router,Link } from 'react-router-dom'
//引入router模块
import RouterView from '../router/Index'
//引入路由配置
import Routes from '../router/Routes'
function App() {
    return (
        <div>
            <Router>
                <Link to='/'>首页</Link>
                <Link to='/my'>我的</Link>
                <RouterView Routes={Routes}></RouterView>
            </Router>
        </div>
    )
}
export default App;

二级路由配置文件

import React, { Component } from 'react';
//导入封装好的路由模块
import RouterView from '../router/Index'
import { BrowserRouter as Router, Link } from 'react-router-dom'
class My extends Component {
    constructor(props) {
        super(props);
        this.state = {}
    }
    render() {
        //获取二级路由的相关信息
        let { Routes } = this.props;
        return (
            <div>
                我的界面
                <Router>
                    {
                        Routes.map((route,index)=>{
                            return (
                                <Link key={index} to={route.path}>{route.name}</Link>
                            )
                        })
                    }
                    <hr />
                    <RouterView Routes={Routes}></RouterView>
                </Router>
            </div>
        );
    }
}
export default My;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值