react基础笔记整理
1、jsx
jsx本质上就是js代码,jsx是写结构的语法糖,会被Babel工具转变为JavaScript代码。
在jsx相关使用
- 可以使用表达式,比如三目,模板字符串,或者简单的数学运算,函数执行等,基本上和vue的差不多
- 根节点只能有一个,如果需要定义一组节点,还不想定义一个父节点包裹,可以使用
<Fragment></Fragment>
包裹,就不会生成了。import React, {Fragment} from 'react';
- 可以嵌套
- 设置属性,比如
style
,<p style=></p>
.另外class需要写成className
,连接属性要改成驼峰的,比如marginLeft
2、组件
react声明组件有两种方式,要么通过函数声明,要么通过类声明组件。
2.1 函数组件
import React from 'react';
import ReactDOM from 'react-dom';
ReactDOM.render(
<div>
你好,react
<People />
</div>,
document.getElementById('root')
)
function People(){
return (
<div>peoplddd</div>
)
}
2.2 类组件
class声明的类组建,需要放到render上面,因为js的规则,function是可以的。
import React from 'react';
import ReactDOM from 'react-dom';
class Man extends React.Component {
render() {
return (
<div>mannnnn</div>
)
}
}
ReactDOM.render(
<div>
你好,react
<Man/>
</div>,
document.getElementById('root')
)
3、组件传值
使用props来传递,和vue类似。不同的是,函数式组件接受数据是用传参,类组件是用this
3.1 函数式传参
使用第一个参数来接受
function People(props){
return (
<div>{props.name}</div>
)
}
ReactDOM.render(
<div>
你好,react
<People name='cat'/>
</div>,
document.getElementById('root')
)
还可以传其他值
<People name='cat' number={666} boo={true} fn={fn => 1}/>
传递子组件
function People(props){
return (
<div>
{props.name}
{props.children}
</div>
)
}
ReactDOM.render(
<div>
你好,react
<People name='cat' children={<div>我是children</div>}/>
</div>,
document.getElementById('root')
)
或者这样也可以
ReactDOM.render(
<div>
你好,react
<People name='cat' >
<div>我是children</div>
</People>
<Man name="said"/>
</div>,
document.getElementById('root')
)
3.2 类组件传参
类组件使用实例接受,也就是用this
class Man extends React.Component {
render() {
return (
<div>{this.props.name}</div>
)
}
}
ReactDOM.render(
<div>
你好,react
<People name='cat'/>
<Man name="said"/>
</div>,
document.getElementById('root')
)
方便起见,我们改成这样
class Man extends React.Component {
render() {
let {name} = this.props;
return (
<div>{name}</div>
)
}
}
3.3 设置默认值
设置组件的默认值使用defaultProps
function People(props){
console.log(props)
return (
<div>
{props.name} age:{props.age}
{props.children}
</div>
)
}
People.defaultProps = {
age: 20
}
3.4 对传参进行类型检查
检查组件参数的类型使用prop-types
import PT from 'prop-types'
function People(props){
return (
<div>
{props.name} age:{props.age}
{props.children}
</div>
)
People.defaultProps = {
age: 20
}
People.propTypes = {
name: PT.string
}
ReactDOM.render(
<div>
你好,react
<People name={20} >
<div>我是children</div>
</People>
<Man name="said"/>
</div>,
document.getElementById('root')
)
这样就成功的报警告了:Warning: Failed prop type: Invalid prop name
of type number
supplied to People
, expected string
in People`
prop-types
的值有下面几种
- PT.array
- bool
- number
- object
- string
- symbol
- node
- element
如果需要设置必填,可以设置isRequired
PT.string.isRequired
4、组件的内部状态 state
只有类组件才有内部状态,其实这个和vue的data
类似。vue是数据驱动,react更多的是状态驱动,每一次修改state对于react都是状态改变,然后去修改值,每次状态改变都会比较新旧的virtual DOM 的结果,对变化的部分进行更新。
修改state的值, 这个和微信小程序的写法相似
this.setState({
name: 'new name'
})
之所以说state是内部状态是因为state完全受控于当前组件,其他组件的state修改也不会影响到当前组件的state。
在一次事件执行期间的所有setState
操作会合并成一次操作,然后更新render
函数。所以在当前事件里获取到的state的值都是改变之前的值。
// state = {a : 1};
onClock(()=>{
this.setSatet({
a: 2
})
console.log(this.state.a) // 1
})
当你想要获取你当前修改的值,可以使用函数式写法,给setState
传入一个函数,它接收两个参数,第一个是你传入的参数,第二个是props,需要有一个返回值,返回你要修改的对象。
// 当前组件叫 Man
<div onClick={()=>{
this.setState({
name: '猫'
}})
this.setState((prev, props)=>{
// 这里获取的是上面设置的值
console.log(prev, props) // {name: "ccccat"} {name: "said"}
return {
name: '又修改了一次'
}
})
}}></div>
// 使用的地方
<Man name="said"/>
5、事件
关于事件的写法上面也已经有了,DOM原生事件会接收一个event
参数,在react里面也有,只不过这个是一个合成对象,react对这个进行处理了。
<div onClick={(event)=>{
console.log(event);
}}>{name}</div>
可以使用event进行阻止冒泡event.stopPropagation()
或者阻止默认行为event.preventDefault()
。
react支持的事件及事件对象更多看文档。
react事件方面最大的问题可能就是this了,this其实是JavaScript老生常谈的话题了,在行内绑定事件的时候如果没有用箭头函数,会找不到this,需要使用bind
绑定以下this到当前实例,最好用箭头函数就好了。
在行内绑定this会有一个问题,就是由于组件每次会重新渲染,然后事件都会被重新注册,本来是没必要重新替换的,由于绑定了this,会重新返回一个事件,react会以为需要重新渲染,多多少少会有一些浪费性能,我们可以在constructor
里面进行绑定
this.handleEvent = this.handleEvent.bind(this);
最好的方案是使用箭头函数或者官网推荐的类属性语法。
this.handleEvent = () => {
// sth
}
6、生命周期函数
装载阶段:
constructor()
:static getDerivedStateFromProps()
:在render之前可修改state的函数,不改变就返回nullrender()
:渲染虚拟DOMcomponentDidMount()
:DOM挂载完成
更新阶段:
static getDerivedStateFromProps()
:父组件更新,子组件可以使用这个进行监控更新shouldComponentUpdate(nextProp, nextState)
:用于性能优化,返回一个true组件正常更新,返回false,视图不会更新。render()
getSnapshptBeforeUpdate(prevProp, prveState)
:需要配合componentDidUpdate
一起使用,获取一个更新之前的快照。它执行的时候已经计算好新的virtual DOM,但是浏览器还未更新DOM。componentDidUpdate(p, s, shot)
:更新已完成的回调,第三个参数是getSnapshptBeforeUpdate
的返回值。
卸载阶段:
componentWillUnmount()
:
错误处理:
- componentDidCatch(error, info): 捕获子组件的生命周期函数内抛出的错误。
7、获取DOM
想要获取真实DOM节点,需要在组件render之后,然后才可以在componentDidMount
函数中拿到。
方式一:ref
<div ref= {(el) => {console.log(el)}}></div>
方式二:createRef()
import React, {createRef} from 'react';
constructor(props){
this.div1 = createRef();
}
render(){
return (
<div ref= {this.div1}></div>
)
}
8、受控组件
当插入一个表单组件,例如input
,渲染完成后输入会正常输入,由于在react中,组件的变化由状态变化导致的,而现在状态没有变,这个input不受react控制,这种组件叫做非受控组件,也就是说它的状态变化不受react控制的,如果想控制它,就需要给input
加一个value
值,一旦加了value
值,只要react不改变value
,input永远不会改变。
<input value=""></input>
监控了input之后的处理
constructor(props){
super(props)
this.state = {
name: 'ccccat',
val: ''
}
}
changeInput = (e) => {
let val = e.target.value;
console.log(val)
this.setState({
val: val
})
}
render() {
let {name, val} = this.state;
return (
<input value={val} onChange={this.changeInput}></input>
)
}
9、路由
使用路由需要安装react-router-dom
入口文件的代码:
import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter as Router, Route } from 'react-router-dom'
import Home from './components/Home'
import User from './components/User'
let root = document.getElementById('root');
ReactDOM.render(
<Router>
<>
<Route path="/" component={Home} />
<Route path="/User" component={User} />
</>
</Router>,
root
)
User.tsx
Home和User内部代码基本上一致:
import React from 'react';
// RouteComponentProps 是定义好的类型,是props的值对应的类型
import { RouteComponentProps } from 'react-router-dom';
type Props = RouteComponentProps & {
// 这里可以写我们自定义的类型
}
// <Props>: 如果上面不引用RouteComponentProps然后也不定义这个的话,在里面是拿不到this.props.location的,只能拿到this.props,因为默认只有一个props,然后props有一个children,类型是一个reactElement,所以需要定义好,然后才可以使用
// this.props返回了三个对象
// 1. history: 历史对象,不是window.history,是内部的一个实现
// 2. location:当前路径的一些信息,与history.location一致
// 3. match:匹配对象
export default class extends React.Component<Props> {
render() {
console.log(this.props.location)
return (
<div>User</div>
)
}
}
10、hooks
让函数组件具有类组件功能的一个东西。
先实现一个class组件,然后用函数组件+hooks改写。
import React from 'react';
class myCount extends React.Component {
state: {
count: 0
}
componentDidMount(){
this.interval = setInterval(()=>{
this.setState({count: this.state.count+1})
}, 1000)
}
componentWillUnmount(){
if(this.interval){
clearInterval(this.interval)
}
}
render(){
return <span>{this.state.count}</span>
}
}
接下来我们改写:
import React, {useEffect, useState} from 'react';
function myCount(){
// 声明一个存储的变量count和修改变量的方法setCount,初始值为0
const [count, setCount] = useState(0);
// 在组件更新完成之后执行回调,在组件销毁的时候执行返回的回调
useEffect(()=>{
const interval = setInterval(()=>{
// setCount(1)
setCount(count => count+1)
}, 1000)
return () => clearInterval(interval)
}, [])
}
10.1 state Hook
这个hook有两个api:useState, useReducer
useState
:传入一个初始值,返回一个数组,需要结构,如上面的例子[count, setCount] = useState(0);
,这个时候count
的值就是0,setCount
就是更新count
的方法,有两种使用方式
- setCount(1):重新给
count
赋值 - setCount(count => count+1):提供一个回调函数,参数是最新的count值。
useReducer
:多接受一个回调,返回值和useState
一致,回调函数内部的逻辑要自己写,这个和redux用法类似
import React, {useEffect, useState, useReducer} from 'react';
function countCallBack(count, action){
switch (action.type){
case 'add':
return count + 1;
case 'minus':
return count - 1;
default:
return count;
}
}
function myCount(){
const [count, dispactch] = useReducer(countCallBack, 0);
useEffect(()=>{
const interval = setInterval(()=>{
dispactch({type: 'add'});
}, 1000)
return () => clearInterval(interval)
}, [])
}
10.2 effect Hook
接受的参数回调的执行是在组件渲染包括重新渲染的时候执行的,回调返回的函数是在组件卸载的时候执行的, 第二个参数是依赖,类似于白名单,里面传入的字段的值修改后才会重新渲染。
useEffect(()=>{
console.log('执行的方法')
return () => console.log('卸载后的回调')
}, [count])
useLayoutEffect
:在渲染前执行的方法,比effect要早。
11、redux
redux是一个单项数据流的管理工具,它的作用:存取公用性数据;
安装:
yarn add redux
使用:
- 创建一个
store/store.js
- 输入以下代码
import { createStore } from 'redux';
// 默认的state
const initialState = {
count: 0
}
/**
* @param {*} state {}
* @param {*} action {type: xx, data: {}};
*/
function reducer(state, action) {
switch (action.type){
case 'add':
// sth
// 这里必须return出一个新的对象
return {count: state.count +1 };
default:
// sth...
return state;
}
}
const store = createStore(reducer, initialState);
console.log(store);
console.log(store.getState());
// 创建一个store
export default store;
- 获取数据:使用
getState()
获取数据store.getState()
- 更新数据: 使用
dispatch
传入一个action。store.dispatch({type: add, data: {count: 2}})
- 引入store:在需要使用
store
的页面引入,如下方法使用
import store from './store/store'
console.log(store.getState());
store.dispatch({ type: 'add' })
console.log(store.getState());
// {count: 0}
// {count: 1}