react学习

React 是一个用于构建用户界面的 JavaScript 库,它只负责应用的视图层,帮助开发人员构建快速且交互式的 web 应用程序。

1 基本使用

1.1 创建虚拟 DOM:

  • React.createElement(component, props, ...children)
  • **JSX **(第一种方式的语法糖)
// 1.创建虚拟DOM
const VDOM1 = React.createElement(
  'h1',
  { id: 'title' },
  React.createElement('span', {}, 'Hello,React1')
);

const VDOM2 = (
  <h1 id="title">
    <span>Hello,React2</span>
  </h1>
);

// 2.渲染虚拟DOM
ReactDOM.render(VDOM2, document.getElementById('root'));

虚拟DOM打印:

1.2 JSX

  1. 定义虚拟 DOM 时,不要写引号。
  2. 标签中混入** JS 表达式**时要用{}
  3. 样式的类名指定不要用 class,要用 className
  4. 内联样式,要用 style={{key:value}} 的形式去写。
  5. 只有一个根标签
  6. 单标签必须闭合
  7. 会自动展开数组
  8. 标签首字母
    1. 若小写字母开头,则将该标签转为 html 中同名元素,若 html 中无该标签对应的同名元素,则报错
    2. 若大写字母开头,react 就去渲染对应的组件,若组件没有定义,则报错。
const myId = '0001';
const myData = 'React';
const data = ['Angular', 'React', 'Vue'];
const VDOM3 = (
  <div>
    <h2 className="title" id={myId.toLowerCase()}>
      <span style={{ color: 'red', fontSize: '29px' }}>
        {myData.toUpperCase()}
      </span>
    </h2>
    <ul>
      {data.map((item, index) => {
        return (
          <li key={index}>
            {index}:{item}
          </li>
        );
      })}
    </ul>
    <input type="text" />
  </div>
);

if 语句以及 for 循环不是 JavaScript 表达式,所以不能在 JSX 中直接使用

表达式:

  • a
  • a+b
  • fn()
  • arr.map()
  • function test(){}

语句:

  • if(){}
  • for(){}
  • switch(){}

1.3 组件化

  • 组件名必须首字母大写

  • 只能有一个根元素,必须有结束标签

  • 函数式组件

function MyComponent1() {
   //此处的this是undefined,因为babel编译后开启了严格模式
  console.log(this);
  return <h2>我是用函数定义的组件(适用于【简单组件】的定义)</h2>;
}
  • 类式组件
class MyComponent2 extends React.Component {
  render() {
    //render是放在哪里的?—— MyComponent的原型对象上,供实例使用。
    //render中的this是谁?—— MyComponent的实例对象 <=> MyComponent组件实例对象。
    console.log('render中的this:', this);
    return <h2>我是用类定义的组件(适用于【复杂组件】的定义)</h2>;
  }
}

渲染类组件标签的基本流程

  • 创建组件实例对象,调用 render 得到虚拟DOM,解析为真实DOM,插入到页面

  • 渲染组件

ReactDOM.render(<MyComponent/>, document.getElementById('root'));

1.4 组件实例三大属性

state

state 是组件对象最重要的属性, 值是对象(可以包含多个 key-value 的组合),不能直接修改或更新

  • 组件中 render 方法中的 this 为组件实例对象

组件自定义的方法中 this 为 undefined,如何解决?

  • 强制绑定 this: 通过函数对象的 bind()
  • 箭头函数
class Toggle extends React.Component {
  constructor(props) {
    super(props);
    this.state = {isToggleOn: true};

    // 为了在回调中使用 `this`,
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      isToggleOn: !prevState.isToggleOn
    }));
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        {this.state.isToggleOn ? 'ON' : 'OFF'}
      </button>
    );
  }
}

使用箭头函数

export default class Toggle extends React.Component {
  state = { isHot: false, wind: '微风' };

  // 箭头函数
  changeWeather = () => {
    const isHot = this.state.isHot;
    this.setState({ isHot: !isHot });
  };
  render() {
    const { isHot, wind } = this.state;
    return (
      <h1 onClick={this.changeWeather}>
        今天天气很{isHot ? '炎热' : '凉爽'},{wind}
      </h1>
    );
  }
}

this指向问题:

class Cat {
  sayThis () {
    console.log(this); // 这里的 `this` 指向谁?
  }
  
  exec (cb) {
    cb(); // 回调函数 没有显示调用者指向 undefined
  }
  
  render () {
    this.exec(this.sayThis); 
  }
}

const tom = new Cat();
tom.render(); // undefined

// ES6 的 class 语法,自动地使用严格模式,非严格模式下指向全局对象
// 当你使用 onClick={this.handleClick}来绑定事件监听函数的时候,
// handleClick 函数实际上会作为回调函数,传入 addEventListener() 
class Demo {
  constructor() {
    this.name = 'demo';
  }
  handleClick1 = () => {
    console.log(this.name);
  };
  handleClick2() {
    console.log(this.name);
  }
}
const demo = new Demo();
const fn1 = demo.handleClick1;
const fn2 = demo.handleClick2;

console.log(demo.handleClick1()); // demo
console.log(demo.handleClick2()); // demo

console.log(fn1()); // demo
console.log(fn2()); // this undefined 

曾经我以为我很懂箭头函数 - 掘金

class 中的方法如果是普通函数方法,该方法会绑定在构造函数的原型上

但是如果方式是箭头函数方法,该方法会绑定实例上

props

  • 通过标签属性从组件外向组件内传递变化的数据
  • 注意: 组件内部不要修改 props 数据,单向数据流
import React from 'react';

export default class Person extends React.Component {
  constructor(props) {
    //构造器是否接收props,是否传递给super,取决于是否希望在构造器中通过this访问props
    super(props);
    console.log('constructor', this.props);
  }
  
  //对标签属性进行类型、必要性的限制
  static propTypes = {
    name: PropTypes.string.isRequired, //限制name必传,且为字符串
    sex: PropTypes.string, //限制sex为字符串
    age: PropTypes.number, //限制age为数值
  };
  
  //指定默认标签属性值
  static defaultProps = {
    sex: '男', //sex默认值为男
    age: 18, //age默认值为18
  };
  
  render() {
    const { name, age, sex } = this.props;
    //props是只读的
    return (
      <ul>
        <li>姓名:{name}</li>
        <li>性别:{sex}</li>
        <li>年龄:{age + 1}</li>
      </ul>
    );
  }
}


//渲染组件到页面
ReactDOM.render(<Person name="jerry"/>,document.getElementById('test1'))

对 props 中的属性值进行类型限制和必要性限制:

  • ~~propTypes ~~方式已弃用
Person.propTypes = {
  name: React.PropTypes.string.isRequired,
  age: React.PropTypes.number
}
  • 使用 prop-types 库进限制(需要引入 prop-types 库)
import PropTypes from 'prop-types';
Person.propTypes = {
  name: PropTypes.string.isRequired,
  age: PropTypes.number.
}

refs

refs 提供了一种方式,允许我们访问 DOM 节点或在 render 方法中创建的 React 元素。

  • React.createRef()
class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}
  • 回调形式
class CustomTextInput extends React.Component {
  constructor(props) {
    super(props);

    this.textInput = null;

    this.setTextInputRef = element => {
      this.textInput = element;
    };
  }

  render() {
    // 使用 `ref` 的回调函数将 text 输入框 DOM 节点的引用存储到 React
    // 实例上(比如 this.textInput)
    return (
      <div>
        <input
          type="text"
          ref={this.setTextInputRef}
        />
      </div>
    );
  }
}

如果 ref 回调函数是以内联函数的方式定义的,在更新过程中它会被执行两次,第一次传入参数 null,然后第二次会传入参数 DOM 元素。这是因为在每次渲染时会创建一个新的函数实例,所以 React 清空旧的 ref 并且设置新的。通过将 ref 的回调函数定义成 class 的绑定函数的方式可以避免上述问题,但是大多数情况下它是无关紧要的。


  • String 类型的 Refs

例如<font style="color:rgb(0, 0, 0);">ref=</font><font style="color:rgb(26, 26, 26);">"textInput"</font>。可以通过<font style="color:rgb(0, 0, 0);"></font><font style="color:rgb(26, 26, 26);">this.refs.textInput</font> 来访问 DOM 节点。不建议使用它,因为 string 类型的 refs 存在 一些问题。它已过时并可能会在未来的版本被移除。

事件处理

  1. 通过onXxx 属性指定事件处理函数(注意大小写)
  • React 使用的是自定义(合成)事件, 而不是使用的原生DOM事件 —————— 为了更好的兼容性
  • React 中的事件是通过事件委托方式处理的(委托给组件最外层的元素) ————————为了的高效
  1. 通过 event.target 得到发生事件的DOM元素对象 ——————————不要过度使用ref

1.5 受控组件

在一个受控组件中,表单数据是由 React 组件来管理的。另一种替代方案是使用非受控组件,这时表单数据将交由 DOM 节点来处理。

  • 非受控组件
class Login extends React.Component{
  handleSubmit = (event)=>{
    event.preventDefault() //阻止表单提交
    const {username,password} = this
    alert(`你输入的用户名是:${username.value},你输入的密码是:${password.value}`)
  }
  render(){
    return(
      <form onSubmit={this.handleSubmit}>
        用户名:<input ref={c => this.username = c} type="text" name="username"/>
        密码:<input ref={c => this.password = c} type="password" name="password"/>
        <button>登录</button>
      </form>
    )
  }
		}
  • 受控组件
class Login extends React.Component {
  
  //初始化状态
  state = {
    username: '', //用户名
    password: '' //密码
  }
  
  //保存用户名到状态中
  saveUsername = (event) => {
    this.setState({ username: event.target.value })
  }
  
  //保存密码到状态中
  savePassword = (event) => {
    this.setState({ password: event.target.value })
  }
  
  //表单提交的回调
  handleSubmit = (event) => {
    event.preventDefault() //阻止表单提交
    const { username, password } = this.state
    alert(`你输入的用户名是:${username},你输入的密码是:${password}`)
  }
  
  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        用户名:<input onChange={this.saveUsername} type="text" name="username" />
        密码:<input onChange={this.savePassword} type="password" name="password" />
        <button>登录</button>
      </form>
    )
  }
    }

2 声明周期

生命周期的三个阶段(旧)

1.初始化阶段: 由 <font style="color:#E8323C;">ReactDOM.render()</font>触发—初次渲染

1. constructor()
2. ~~componentWillMount()~~
3. render()
4. componentDidMount()

2.更新阶段: 由组件内部<font style="color:#E8323C;">this.setSate()</font>或父组件重新 render 触发

a. shouldComponentUpdate()

b. ~~componentWillUpdate() ~~ // 组件将要更新的钩子

c. render()

d. componentDidUpdate() // 组件更新完毕的钩子

  1. 卸载组件: 由<font style="color:#E8323C;">ReactDOM.unmountComponentAtNode()</font>触发

a. componentWillUnmount()

生命周期的三个阶段(新)

1. 初始化阶段: 由 <font style="color:#E8323C;">ReactDOM.render()</font>触发—初次渲染

a. constructor()

b. **getDerivedStateFromProps **若state的值在任何时候都取决于props,那么可以使用

c. render()

d.** componentDidMount() : **组件挂载完毕的钩子,一般在这个钩子中做一些初始化的事,例如:开启定时器、发送网络请求、订阅消息

  1. 更新阶段: 由组件内部<font style="color:#E8323C;">this.setSate()</font>或父组件重新 render 触发

a. **getDerivedStateFromProps **

b. shouldComponentUpdate() 控制组件更新的“阀门”

c. render()

d. **getSnapshotBeforeUpdate **在更新之前获取快照

c. componentDidUpdate() 组件更新完毕的钩子

  1. 卸载组件: 由<font style="color:#E8323C;">ReactDOM.unmountComponentAtNode()</font>触发

  2. componentWillUnmount(): 组件将要卸载的钩子,一般在这个钩子中做一些收尾的事,例如:关闭定时器、取消订阅消息

      shouldComponentUpdate(){
				console.log('Count---shouldComponentUpdate');
				return true
			}

重要的勾子:

  1. render:初始化渲染或更新渲染调用
  2. componentDidMount:开启监听, 发送 ajax 请求
  3. componentWillUnmount:做一些收尾工作, 如: 清理定时器

即将废弃的勾子:

  1. componentWillMount
  2. componentWillReceiveProps
  3. componentWillUpdate

3 setState

setState(stateChange, [callback])------对象式的setState

  • stateChange为状态改变对象(该对象可以体现出状态的更改)
  • callback是可选的回调函数, 它在状态更新完毕、界面也更新后(render调用后)才被调用

setState(updater, [callback])------函数式的setState

  • updater 为返回stateChange对象的函数。
  • updater 可以接收到state和props。
  • callback 是可选的回调函数, 它在状态更新、界面也更新后(render调用后)才被调用。

总结:

1.对象式的setState是函数式的setState的简写方式(语法糖)

2.使用原则:

(1).如果新状态不依赖于原状态 ===> 使用对象方式

(2).如果新状态依赖于原状态 ===> 使用函数方式

(3).如果需要在setState()执行后获取最新的状态数据,要在第二个callback函数中读取

4 Hooks

Hooks 是React 16.8.0版本增加的新特性/新语法,可以让你在函数组件中使用 state 以及其他的 React 特性

三个常用的Hook

  • State Hook: React.useState()
  • Effect Hook: React.useEffect()
  • Ref Hook: React.useRef()

State Hook

  1. State Hook让函数组件也可以有 state 状态, 并进行状态数据的读写操作

  2. 语法:const [xxx, setXxx] = React.useState(initValue)

  3. useState()说明:

     参数: 第一次初始化指定的值在内部作缓存
    
     返回值: 包含2个元素的数组, 第1个为内部当前状态值, 第2个为更新状态值的函数
    
  4. setXxx()2种写法:

    1. setXxx(newValue): 参数为非函数值, 直接指定新的状态值, 内部用其覆盖原来的状态值
    2. setXxx(value => newValue): 参数为函数, 接收原本的状态值, 返回新的状态值, 内部用其覆盖原来的状态值

Effect Hook

Effect Hook 可以让你在函数组件中执行副作用操作(用于模拟类组件中的生命周期钩子)

React中的副作用操作:

  • 发ajax请求数据获取
  • 设置订阅 / 启动定时器
  • 手动更改真实DOM
useEffect(() => { 
    // 在此可以执行任何带副作用操作
    return () => { // 在组件卸载前执行
    // 在此做一些收尾工作, 比如清除定时器/取消订阅等
  }
}, [stateValue]) // 如果指定的是[], 回调函数只会在第一次render()后执行

可以把 useEffect Hook 看做如下三个函数的组合:

  • componentDidMount()
  • componentDidUpdate()
  • componentWillUnmount()

Ref Hook

  • Ref Hook 可以在函数组件中存储/查找组件内的标签或任意其它数据
  • 语法: const refContainer = useRef()
  • 作用:保存标签对象,功能与React.createRef()一样

5 组件通信方式总结

几种通信方式:

  1. props:
  • children props
  • render props
  1. 消息订阅-发布:pubs-sub、event等等
  2. 集中式管理:redux、dva等等
  3. context: 生产者-消费者模式

最佳实践:

  • 父子组件:props
  • 兄弟组件:消息订阅-发布、集中式管理
  • 祖孙组件(跨级组件):消息订阅-发布、集中式管理、context(开发用的少,封装插件用的多)

6 路由

下载:npm install react-router-dom

5.1.1 路由基本使用

// App.js
import React from 'react';
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
function Index() {
  return <div>首页</div>;
}
function News() {
  return <div>新闻</div>;
}
function App() {
  return (
    <Router>
      <div>
        <Link to="/index">首页</Link>
        <Link to="/news">新闻</Link>
      </div>
      <div>
        <Route path="/index" component={Index}/>
        <Route path="/news" component={News}/>
      </div>
    </Router>
  );
}

5.1.2 路由嵌套

function News(props) {
  return (
    <div>
      <div>
        <Link to={`${props.match.url}/company`}>公司新闻</Link>
        <Link to={`${props.match.url}/industry`}>行业新闻</Link>
      </div>
      <div>
        <Route path={`${props.match.path}/company`} component={CompanyNews} />
        <Route path={`${props.match.path}/industry`} component={IndustryNews}/>  
      </div>	
    </div>
  );
}

function CompanyNews() {
  return <div>公司新闻</div>
}
function IndustryNews() {
  return <div>行业新闻</div>
}
5.1.3 路由传参
import url from 'url';
class News extends Component {
  constructor(props) {
    super(props);
    this.state = {
      list: [{
        id: 1,
        title: '新闻1'
      }, {
        id: 2,
        title: '新闻2'
      }]
    }
  }
  
  render() {
    return (
      <div>
        <div>新闻列表组件</div>
        <ul>
          this.state.list.map((item, index) => {
            return (
              <li key={index}>
                <Link to={`/detail?id=${item.id}`}>{item.title}</Link>
              </li>
            );
          })
        </ul>
      </div>
    );
  }
}
class Detail extends Component {
  constructor(props) {
    super(props);
  }
  const { query } = url.parse(this.props.location.search, true);
console.log(query); // {id: 1}
render() {
  return <div>新闻详情</div>
}
}

5.1.4 路由重定向

import { Redirect } from 'react-router-dom';

class Login extends Component {
  render() {
    if (this.state.isLogin) {
      return <Redirect to="/"/>
    }
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值