react使用

  • 1.React概述
  • 1.1 什么是React?
  • 1.2 React特点
  • 2.React的基本使用
  • 2.1 React的安装
  • 2.2 React的使用
  • 3.React脚手架的使用
  • 3.1 使用React脚手架初始化项目
  • 3.2 在脚手架中使用React
  • 3.3 JSX的基本使用
  • 3.4 组件
  • 3.5 React事件处理
  • 3.6 组件中state和setState
  • 3.7 表单处理
  • 4.组件通讯
  • 4.1 组件的props
  • 4.2 组件通讯的三种方式
  • 4.3 props深入
  • 4.4 props校验
  • 4.5 组件的生命周期
  • 5.Redux
  • 5.1 Redux概述
  • 5.2 Redux三大核心
  • 5.3 Redux的组成
  • 5.4 React-Redux


1.React概述

1.1 什么是React?

React是一个用于构建用户界面的JavaScript库。
React主要用来写HTML页面,或构建Web应用。
如果从MVC的角度来看,React仅仅是视图层(V),也就是只负责视图的渲染,而并非提供了完整的M和C的功能。
React起源于Facebook的内部项目,后又用来假设Instagram的网站,并与2013年5月开源。

1.2 React特点

  • 声明式
  • 基于组件
  • 学习一次,随处使用
    使用React可以开发Web应用;
    使用React可以开发移动端原生应用(react-native);
    使用React可以开发VR(虚拟现实)应用(react360)。

2.React的基本使用

2.1 React的安装

安装命令:npm i react react-dom
react包是核心,提供创建元素、组件等功能
react-dom包提供DOM相关功能等

2.2 React的使用

1.引入react和react-dom两个js文件

<script src="https://unpkg.com/react@17/umd/react.development.js" crossorigin></script>
<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>
// 或者
<script src="./node_modules/react/umd/react.development.js"></script>
<script src="./node_modules/react-dom/umd/react-dom.development.js"></script>

2.创建React元素

const title = React.createElement('h1',null,'Hello React !!!')

3.渲染React元素到页面中

ReactDOM.render(title, document.getElementById('root'))

3.React脚手架的使用

3.1 使用React脚手架初始化项目

1.安装命令
推荐使用:npx create-react-app my-app
npm init react-app my-app
yarn create react-app my-app

2.npx命令介绍
npm v5.2.0引入的一条命令。
目的:提升包内提供的命令行工具的使用体验。
原来:先安装脚手架包,再使用这个包中提供的命令。
现在:无需安装脚手架包,就可以直接使用这个包提供的命令。

3.yarn介绍
yarn是Facebook发布的包管理器,可以看作是npm的替代品,功能与npm相同。
yarn具有快捷、可靠和安全的特点。
初始化新项目:yarn init
安装包:yarn add 包名称

4.启动
npm run start

3.2 在脚手架中使用React

1.导入react和react-dom两个包。
2.调用React.createElement()方法创建react元素。
3.调用ReactDOM.render()方法渲染react元素到页面中。

import React from 'react'
import ReactDom from 'react-dom'
const title = React.createElement('h1',null,'Hello React !!!')
ReactDOM.render(title, document.getElementById('root'))

4.为什么脚手架可以使用JSX语法?
JSX不是标准的ECMAScript语法,它是ECMAScript的语法扩展。
需要使用babel编译处理后,才能在浏览器环境中使用。
create-react-app脚手架中已经默认有该配置,无须手动配置。
编译JSX语法的包为:@babel/preset-react。

3.3 JSX的基本使用

1.注意点
React元素的属性名使用驼峰命名法;
特殊属性名:class -> className、for -> htmlFor、tabindex -> tabIndex。
没有子节点的React元素可以用/>结束。
推荐:使用小括号包裹JSX,从而避免JS中的自动插入分号陷阱。

// 使用小括号包裹JSX
const dv = (
	<div>Hello JSX</div>
)

2.JSX中使用JavaScript表达式
嵌入JS表达式:
数据存储在JS中
语法:{ JavaScript 表达式}

const name = 'Jack'
const dv = (
	<div>
		Hello!{name}
		{loadData()}
	</div>
)

3.JSX的条件渲染
实现方式:
if/else
三元运算符
逻辑与运算符&&(只有&&前面语句为真才能执行后面的)

4.JSX的列表渲染
语法:数组的map()方法
注意:渲染列表时应添加key属性,key的值要保证唯一,尽量避免使用索引号作为key。

const peopleList = [
  {id: 1, name: '张三'},
  {id: 2, name: '李四'},
  {id: 3, name: '小王'}
]
const list = (
	<div className="list">
        {peopleList.map(item => <div key={item.id}>{item.name}</div>)}
    </div>
)

5.JSX的样式处理
(1)行内样式

<h1 style={{ color: 'red', backgroundColor: 'skyblue'}}>
	JSX的样式处理
</h1>

(2)类名——className(推荐)

<h1 className="title">
	JSX的样式处理
</h1>

6.JSX总结
JSX是React的核心内容。
JSX在JS代码中写HTML结构,是React声明式的体现。
使用JSX配合嵌入的JS表达式、条件渲染、列表渲染,可以描述任意结构。
推荐使用className的方式给JSX添加样式。
React完全利用JS语言自身的能力来编写UI,而不是造轮子增强HTML功能。

3.4 组件

1.组件的创建方式
(1)使用函数创建,叫做无状态组件,只负责数据的展示(静),无state和生命周期,动态数据都是通过父组件传值子组件通过props接收渲染。
使用JS的函数或箭头函数创建。
函数名必须以大写字母开头。
函数组件必须有返回值,表示该组件的结构,如果返回为null,表示不渲染任何内容。
使用函数名作为组件标签名。

function Hello() {
	return(
		<div>这是我创建的第一个函数组件!</div>
	)
}
// 使用箭头函数创建
const Hello = () => <div>这是我创建的第一个函数组件!</div>
ReactDOM.render(<Hello/>, document.getElementById('root'))

(2)使用类class创建,叫做有状态组件,有生命周期,有this和state。
类组件:使用ES6的class创建的组件。
类名称必须以大写字母开头。
类组件应该继承React.Component父类,从而可以使用父类中提供的方法和属性。
类组件必须使用render()方法,render()方法必须有返回值,表示该组件的结构。
注意:使用定义的变量和方法,必须要加上this,而使用第一种方式定义的不需要使用this,但是第一种定义需要使用const,第二种不需要。

class Hello extends React.Component {
  render() {
    return <div>这是我创建的第一个函数组件!</div>
  }
}
ReactDOM.render(<Hello />, document.getElementById('root'))

注:大部分建议使用无状态组件,因为如果大量用有状态的组件容易触发生命周期和钩子函数,页面会出面加载慢等问题。
(3)抽离为独立的JS文件
组件需要导出,在另一个文件导入即可使用。

export default Hello;
import Hello from "Hello.js";

3.5 React事件处理

1.事件绑定
React事件绑定的语法与DOM事件语法相似。
语法:on+事件名称={事件处理程序},比如:onClick = {() => {}}
注意:React事件采用驼峰命名法,比如:onMouseEnter、onFocus。
在函数组件中绑定事件:

class Hello extends React.Component {
  handleClick() {
    console.log('单击事件触发了')
  }
  render() {
    return (
      <button onClick={this.handleClick}>点击</button>
    )
  }
}
ReactDOM.render(<Hello />, document.getElementById('root'))
export default Hello;

2.事件对象
可以通过事件处理程序中的参数获取到事件对象。
React中的事件对象叫做:合成事件(对象)。
合成事件:兼容所有浏览器,无须担心跨浏览器兼容性问题。

class Hello extends React.Component {
  handleClick(e) {
    e.preventDefault()
    console.log('单击事件触发了')
  }
  render() {
    return (
      <a href="http://www.baidu.com" onClick={this.handleClick}>点我,不会跳转页面</a>
    )
  }
}
ReactDOM.render(<Hello />, document.getElementById('root'))
export default Hello;

3.6 组件中state和setState

1.state的基本使用
状态state即数据,是组件内部的私有数据,只能在组件内部使用。
state的值是对象,表示一个组件中可与有多个数据。
通过this.state来获取状态。

class Hello extends React.Component {
    // 完整写法
    constructor(){
        super()
        // 初始化state
        this.state = {
            count: 0
        }
    }

    // 简化写法
    state = {
        count: 0
    }
    render() {
      return (
        <div>点击,{this.state.count}</div>
      )
    }
}

2.setState()修改状态
语法:this.setState({要修改的数据})
作用:修改state,更新UI
思想:数据驱动视图
注意:不能直接修改state的值,必须要用setState修改。

this.setState({
	count: this.state.count+1
})

3.事件绑定的this指向
(1)箭头函数
特点:箭头函数自身不绑定this,this指向外部环境。

// 写法1
handleClick() {
	// 此处this指向render方法
   	this.setState({
        count: this.state.count+1
    })
}
render() {
	return (
		<div onClick={() => this.handleClick()}>点击{this.state.count}</div>
	)
}

// 写法2
handleClick = () => {
    this.setState({
        count: this.state.count+1
    })
}
render() {
    return (
    	<div onClick={this.handleClick}>点击{this.state.count}</div>
    )
}

(2)Function.prototype.bind()
直接使用JS函数,函数中this的值会是undefined。
解决方法:利用ES5中的bind()方法,将事件处理程序中的this与组件实例绑定到一起。

class Hello extends React.Component {
    constructor(){
        super()
        // 初始化state
        this.state = {
            count: 0
        }
        this.handleClick = this.handleClick.bind(this)
    }
    
    handleClick() {
        this.setState({
            count: this.state.count+1
        })
    }
    render() {
		return (
			<div onClick={this.handleClick}>点击{this.state.count}</div>
		)
    }
}

3.7 表单处理

1.受控组件(推荐使用)
HTML中的表单元素是可输入的,也就是有自己的可变状态。
而,React中的可变状态通常保存在state中,并且只能通过setState()方法来修改。
React将state与表单元素值value绑定到一起,由state的值来控制表单元素的值。
受控组件:其值受到React控制的表单元素。
总结:文本框、富文本框、下拉框,操作value属性。
复选框:操作checked属性。

{/* 文本框 */}
<input type="text" value={this.state.txt} onChange={this.handleInput}/>
{/* 富文本框 */}
<textarea value={this.state.content} onChange={this.handleContent}></textarea>
{/* 下拉框 */}
<select value={this.state.city} onChange={this.handleCity}>
    <option value="sh">上海</option>
    <option value="bj">北京</option>
    <option value="gz">广州</option>
</select>
{/* 复选框 */}
<input type="checkbox" value={this.state.isChecked} onChange={this.handleChecked}/>

state = {
    txt:'',
    content: '',
    city: '',
    isChecked: false
}
handleInput = (e) => {
    this.setState({
        txt: e.target.value
    })
}
handleContent = (e) => {
    this.setState({
        content: e.target.value
    })
}
handleCity = (e) => {
    this.setState({
        city: e.target.value
    })
}
handleChecked = (e) => {
    this.setState({
        isChecked: e.target.checked
    })
    // console.log(this)
}

优化:
给表单元素添加name属性,名称与state定义的相同。
根据表单元素类型获取对应的值。
在change事件处理程序中通过[name]来修改对应的state。

<div>
    {/* 文本框 */}
    <input type="text" name="txt" value={this.state.txt} onChange={this.handleChange}/>
    {/* 富文本框 */}
    <textarea name="content" value={this.state.content} onChange={this.handleChange}></textarea>
    {/* 下拉框 */}
    <select name="city" value={this.state.city} onChange={this.handleChange}>
        <option value="sh">上海</option>
        <option value="bj">北京</option>
        <option value="gz">广州</option>
    </select>
    {/* 复选框 */}
    <input type="checkbox" name="isChecked" value={this.state.isChecked} onChange={this.handleChange}/>
</div>

handleChange = (e) => {
    // 获取当前DOM对象
    const target = e.target
    // 根据类型获取值
    const value = target.type === 'checkbox' ? target.checked : target.value
    // 获取nama
    const name = target.name
    this.setState({
        [name]: value
    })
}

2.非受控组件
(1)调用React.createRef()方法创建一个ref对象

constructor(){
    super()
    this.txtRef = React.createRef()
}

(2)将创建好的ref对象添加到文本框中

<input type="text" ref={this.txtRef} onChange={this.handleChange}/>

(3)通过ref对象获取到文本框的值

handleChange = () => {
    console.log(this.txtRef.current.value)
}

4.组件通讯

4.1 组件的props

组件是封闭的,要接收外部数据应该通过props来实现;
props作用:接收传递组件的数据;
传递数据:给组件标签添加属性;
接收数据:函数组件通过参数props接收数据,类组件通过this.props接收数据。
例如:
(1)函数组件

<Hello name="Jack" age={18} />

function Hello(props) {
	console.log(props)
	return (
		<div>接收到的数据:{props.name}</div>
	)
}

(2)类组件

<Hello name="Jack" age={18} />

class Hello extends React.Component {
	render() {
		return (
			<div>接收到的数据:{this.props.name}</div>
		)
	}
}

props特点:
(1)可以给组件传递任意类型的数据;
(2)props是只读的对象,只能读取属性的值,无法修改对象;
(3)注意:使用类组件时,如果写了构造函数,应该将props传递给super(),否则无法在构造函数中获取到props,值为undefined。

constructor(props) {
  super(props)
  console.log(props)
}
<Hello
	name="Jack"
	age={18}
	colors={['red', 'green', 'blue']}
	fn={() => console.log('这是一个函数')}
	tag={<p>这是一个p标签</p>}
/>

4.2 组件通讯的三种方式

1.父组件传递数据给子组件
给子组件标签添加属性,子组件通过props接收父组件传递的数据,具体操作见4.1。

2.子组件传递数据给父组件
父组件提供一个回调函数,用于接收数据,将该函数作为属性的值,传递给子组件,子组件再通过props调用此函数将参数传过去。

// 父组件
getChildMsg = (msg) => {
	console.log('接收子组件数据', msg)
}
<Child getMsg={this.getChildMsg}/>

// 子组件
this.props.getMsg('参数a')

3.兄弟组件
将共享状态提升到最近的公共父组件中,由公共父组件管理这个状态。
思路:状态提升。
公共父组件职责:提供共享状态,提供操作共享状态的方法。
要通讯的子组件只要通过props接收状态或操作状态的方法。

4.Context跨组件传递数据
(1)创建Provider(提供数据)和Consumer(消费数据)两个组件。
(2)使用Provider作为父节点,值放在value传递过去
(3)子组件调用Consumer组件使用data接收数据
实例:

// 兄弟组件通信
import React, { Component } from 'react';
const {Provider, Consumer} = React.createContext()

class Login extends Component {
  render() {
    return(
      <Provider value="pink11">
        <div className="App">
          <Node />
        </div>
      </Provider>
    )
  }
}

const Node = props => {
  return (
    <div className="node">
      <Consumer>
        {data => <span>数据--{data}</span>}
      </Consumer>
    </div>
  )
}

4.3 props深入

children属性
表示组件标签的子节点,当组件标签有子节点时,props就会有该属性。
children属性与普通的props一样,值可以是任意值(文本、React元素、组件,甚至是函数)

function Hello(props) {
    return(
        <div>
            组件的子节点:{props.children}
        </div>
    )
}

<Hello>
    <p>我是子节点</p>
</Hello>

4.4 props校验

1.安装prop-types
yarn add prop-types
npm i prop-types

2.导入prop-types

3.使用组件名propTypes={}来给组件添加校验规则

4.校验规则通过PropTypes对象来指定

import PropTypes from 'prop-types'
function App(props){
    return (
        <h1>Hi, {props.colors}</h1>
    )
}
App.propTypes = {
    a: PropTypes.number,
    fn: PropTypes.func.isRequired,
    tag: PropTypes.element,
    filter: PropTypes.shape({
        area: PropTypes.string,
        price: PropTypes.number
    })
}

5.约束规则
常见类型:array、bool、func、number、object、string、symbol
React元素类型:element
必填项:isRequired
特定结构的对象:shape({})

optionalFunc: PropTypes.func,
requiredFunc: PropTypes.func.isRequired,
optionalObjectWithShape: PropTypes.shape({
    color: PropTypes.string,
    fontSize: PropTypes.number
})

4.5 组件的生命周期

组件的生命周期:组件从被创建到挂载到页面中运行,再到组件不用时卸载的过程。
只有类组件才有生命周期。
1.创建时(挂载阶段)
constructor() --> render() --> componentDidMount()
constructor:创建组件时最先执行,作用:初始化state,为事件处理程序绑定this
render:每次组件渲染都会触发,作用:渲染UI,注意不能调用setState()
componentDidMount:组件挂载(完成dom渲染)后,作用:发送网络请求,DOM操作

2.更新时(更新阶段)
执行时机:setState()、forceUpdate()、组件接收到新的props。
说明:以上三者任意一种变化,组件就会重新渲染。
render() --> componentDidUpdate()
componentDidUpdate:组件更新(完成dom渲染)后,作用:发送网络请求,DOM操作

3.卸载
componentWillUnmount:清除定时器

5.Redux

5.1 Redux概述

Redux是一个用于JavaScript状态容器,提供可与预测化的状态管理。
Redux可以让你构建一致化的应用,运行于不同的环境(客户端、服务器、原生应用),并且易于测试。
React除了和React一起使用外,还支持其他界面库,而且它体积小(2kb)。
Redux与React之间是没有关系的,Redux支持React、Angular、jQuery甚至是JavaScript。

5.2 Redux三大核心

1.单一数据源头store
2.State是只读的,只能通过触发action去改变:

store.dispatch({
    type: 'add',
    index: 1
})

3.使用纯函数来执行修改
为了描述action如何改变state,需要用到reducers。
reducers只是一些纯函数,它接收先前的state和action,并且返回新的state,可以复用、可以控制顺序、传入附加参数。

5.3 Redux的组成

组件 -> store.dispatch()发送action -> reducer -> store
1.State-状态(可以理解为数据)
state分类:
DomainDate:服务端的数据,比如:获取用户信息、商品的列表等等;
UI State:关于当前组件的state,比如:弹框的显示隐藏,受控组件等等;
App State:全局的state,比如:当前是否请求loading、当前路由信息等可能被多个和组件去使用的到的状态。

2.Action-事件
Action是把数据从应用传到store的载体,它是store数据的唯一来源,通过store.dispatch()将action传给store。
Action特点:
Action本质是一个js对象,对象内部必须有一个type属性来表示要执行的动作。
Action只是描述了有事情要发生,并没有描述如何去更新state。

3.Reducer
Reducer本质就是一个函数,用来响应发送过来的action,然后经过处理,把state发送给store。
注意:在Reducer函数中,必须要有return返回值,这样store才能接收到数据。
函数会接收两个参数,第一个是初始化state,第二个是action。

const initState = {...}
rootReducer = (state = initState, action) => {
    ... return {...}
}

4.Store
Store是把action和reducer联系到一起的对象。
主要职责:
维持应用的state;
提供getState()方法获取state;
提供dispatch()方法发送action;
提供subscribe()来注册监听;
提供subscribe()返回值来注销监听;

import { createStore } from 'redux'
const store = createStore(传递reducer)

5.Redux的使用
(1)创建store文件夹,存放index.js、reducer.js、action.js
index.js

import { createStore } from 'redux'
import { reducer } from './reducer'
const store = createStore(reducer)
export default store

reducer.js

const initState = {
  name:'默认的initState'
}
const reducer = (state = initState, action) => {
  switch(action.type){
    case 'send':
      return Object.assign({}, state, action);
    default:
      return state;
  }
}

module.exports = { 
  reducer 
}

action.js

const sendAction = () => {
  console.log('调用了sendAction--')
  return {
    type: 'send',
    value: 'send发送了'
  }
}

module.exports = { 
  sendAction 
}

(2)创建组件A

import React, { Component } from 'react';
import store from '../store'
import {sendAction} from '../store/action'

class ComA extends Component {
  handle = () => {
    store.dispatch(sendAction())
  }
  componentDidMount() {
    store.subscribe(()=>{
      // 接收数据
      console.log('subscribe---', store.getState())
    })
  }
  render() {
    return (
      <div>
        <button onClick={this.handle}>点击</button>
      </div>
    )
  }
}

export default ComA;

5.4 React-Redux

1.React-Redux中两个最重要的成员
Provider:
Provider包裹在根组件最外层,使所有子组件都可以拿到state。
Provider接收store作为props,然后通过context往下传递,这样react中任何组件都可以通过context获取到store。
connect:
connect使组件跟store来进行关联。

2.安装
npm install redux
npm install react-redux

yarn add redux
yarn add react-redux

3.React-Redux的使用
(1)创建store文件夹,存放index.js、reducer.js、action.js
index.js

import { createStore } from 'redux'
import { reducer } from './reducer'
const store = createStore(reducer)
export default store

reducer.js

const initState = {
  count:0
}
const reducer = (state = initState, action) => {
  switch(action.type){
    case 'add':
      return {
        count: state.count + 1
      };
    case 'plus':
      return {
        count: state.count - 1
      };
    default:
      return state;
  }
}

module.exports = { 
  reducer 
}

action.js

const mapDispatchToProps = dispatch => {
  return {
    addAction:()=>{
      dispatch({
        type: 'add'
      })
    },
    plusAction:()=>{
      dispatch({
        type: 'plus'
      })
    }
  }
}

const mapStateToProps = state => {
  return state
}

module.exports = {
  mapDispatchToProps,
  mapStateToProps
}

(2)在根组件使用Provider

import React, { Component } from 'react';
import ComA from './ComA'
import ComB from './ComB'
import store from '../store1'
import {Provider} from 'react-redux'

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <div className="App">
          <ComA></ComA>
          <ComB></ComB>
        </div>
      </Provider>
    )
  }
}

export default App;

(3)创建组件A,用来传递数据

import React, { Component } from 'react';
import {connect} from 'react-redux'
import {mapDispatchToProps} from '../store1/action'

class ComA extends Component {
  add = () => {
    this.props.addAction()
  }
  plus = () => {
    this.props.plusAction()
  }
  render() {
    console.log(this.props)
    return (
      <div>
        <button onClick={this.add}>+</button>
        <button onClick={this.plus}>-</button>
      </div>
    )
  }
}

export default connect(null, mapDispatchToProps)(ComA);

(4)创建组件B,用来接收数据

import React, { Component } from 'react';
import {connect} from 'react-redux'
import {mapStateToProps} from '../store1/action'

class ComB extends Component {
  render() {
    return (
      <div>{this.props.count}</div>
    )
  }
}

export default connect(mapStateToProps)(ComB);