react 笔记

cdn 初识react

  1. 先下载 react react-dom (npm install react react-dom)
  2. 引入并使用
<body>
  <div class="test">

  </div>
  <script src="./node_modules/react/umd/react.development.js"></script>
  <script src="./node_modules/react-dom/umd/react-dom.development.js"></script>

  <script>
    const res=React.createElement("h1",null,"hello react")
 //第一个参数为标签名  第二个为标签属性{id:test,class:test} 第三个及以后为子元素    ReactDOM.render(res,document.getElementsByClassName("test")[0])
//第一个为创建的react元素  第二个是容器
  </script>

react脚手架 初识

  1. npx create-react-app my-app (创建项目 npx相较于npm有避免全局污染,减少存储空间等优点)
    create-react-app就是react脚手架
  2. cd my-app / npm start
    可以发现my-app里面的src/index.js 里的内容和CDN方式有相似之处 先导入react/react-dom 然后再创建虚拟dom 之后渲染

认识jsx

jsx是在js代码中写xml(html)
在react中使用jsx的目的就是为了简化创建dom的过程
在原生js中是不能直接使用jsx语法的在react脚手架中,使用了Babel进行转换

react style样式

import React from 'react';
import ReactDOM from 'react-dom';
import "./index.css" //这里通过类名来改样式
const isloading = false
const showmsg=()=>{
  if(isloading){
    return <div>loading...</div>
  }
  return <div>finish...</div>
}
const res =(
  <div className='test' style={{backgroundColor:"red",fontSize:"20px"}}> //这个是具体例子 第二个是通过对象来改样式
    show...
    {showmsg()}
  </div>
)

ReactDOM.render(res,document.getElementById("root"))Te

react组件

react 有俩种形式组件,一种是函数式,一种是类式组件
函数式组件又称为无状态组件,类式组件又称为有状态组件

function Test(){
  return (<div>test</div>)
}'
ReactDOM.render(<Test/>,document.getElementByID("root"))

class Test extends React.component{
  render(){
    return(<div>test</div>)
  }
}

react 绑定(添加)事件

on+click = onClick
on +mouseover = onMouseOver
...

function Ho(params) {
  function show(){
    console.log('show2');
  }
  return(<div onClick={show}>eee</div>)
}
ReactDOM.render(<Ho/>,document.getElementById("root"))

class Res2 extends  React.Component{
  show(params) {
    console.log('show');
  }
  render(){
    return (
      <div onClick={this.show}>123</div>
    )
  }
}

state 和setstate

state = {count:0,name:'zql'}
addcount = ()=>{
    this.setState({
      count:this.state.count+1
    })
  }
  render(){
    return (
      <div>
        <div onClick={this.show}>123{this.state.count}{this.state.name}</div>
        <button onClick={this.addcount}>count++</button>
      </div>
    )
  }

受控组件

什么是受控组件,就是其中的状态受到state的管理
比如input的value可以设为state中的值,改变时通过onChange监听用setState来改变值

非受控组件

一般通过ref来获取

首先创建 myref = React.createRef()
然后绑定<input ref={this.myref} value="123"></input>
之后获取数据 
 <button onClick={this.handler5}> get</button>
  handler5= ()=>{
    console.log(this.myref.current.value);
  }

props 传值

函数组件接受用参数 该参数为对象
类组件接受用this.props 也是对象
props是只读对象,在类组件中的constructor函数要使用props,得:
constructor(props){
    super(props)
    console.log(props);
  }

function Test(props) {
  return (
    <div>
      name:{props.name}
      age:{props.age}
    </div>
  )
}

class Test2 extends React.Component{
  render(){
    return(
      <div>

        name:{this.props.name}
      
        age:{this.props.age}
      </div>
    )
  }
}
ReactDOM.render(<Test2 name="zql" age="18"/>,document.getElementById('root'))

组件传值 通信

  1. 父传子通过props 类似上面的
  2. 子穿父
由于父组件要拿到子组件的值  so父组件的提供一个渠道让子组件将值传递给父组件
一般 通过父组件提供的函数传递给子组件  子组件在合适的时候调用该函数  通过传递参数从而达到传参的效果
class Test extends React.Component{
  state={
    msg:"msg from small component"
  }
  postmsg=()=>{
    this.props.getmsg(this.state.msg)
  }
  render(){
    return (
      <div>
      <div>i am the small</div>
      <button onClick={this.postmsg}>post to big</button>
    </div>
    )

  }
}

class Test2 extends React.Component{
  getMsgFromSmall=(msg)=>{
    this.setState({msg})
  }
  state={
    name:"zql",
    age:"12",
    msg:""
  }
  render(){
    return(
      <div>
        i am the big one
        msg:{this.state.msg}
        <Test getmsg={this.getMsgFromSmall}></Test>       
      </div>
    )
  }
}
  1. 兄弟组件之间值的交流
    思想主要是状态提升,将所使用的值提升到父组件,父组件提供操作该值的方法。
    4.祖->孙组件之间值的交流
思想主要是借助  React.createContext()
通过解构获得俩个组件 const {Provider,Consumer} =React.createContext()

一般将要传递的数据这要搞
<Provider value={this.state}>   //value 包含了要传递的值
        <div>
        i am the big one
        msg:{this.state.msg}
        <Test getmsg={this.getMsgFromSmall}></Test>       
      </div>
      </Provider>
接受用到
 <Consumer>
          {data=>console.log(data)}
        </Consumer>

5.消息发布订阅机制 PubSub
import PubSub from 'pubsub-js'
PubSub.publish("ABC",{name:'zql',age:22}) //发布消息
this.token=PubSub.subscribe("ABC",function(_,data){ //订阅消息
console.log(data);
})

PubSub.unsubscribe(this.token)//一般在组件卸载时要取消订阅

props.children

如果组件内有东西。那么该组件props对象就有一个children属性

prop-types

对于组件来说 通过props 得到的数据是外来数据,因此很有必要对这些数据做一些规范性限制
要给组件props加规范的这样搞

npm install prop-types
App..propTypes={
  name:PropTypes.string,
  data:PropTypes.array
}

常见props限制:
PropTypes.string.isRequired 表字符串且必传
PropTypes.shape({
name:PropTypes.string,
age:PropTypes.array.number
})

另外有些情况下需要给props默认值
App.defaultProps={
age:"19"
}

react生命周期

生命周期指组件被创建到被卸载的时间,在这期间有很多函数可以被调用,这就是钩子函数
生命周期概念是在类组件的基础上谈论的,函数组件没有生命周期,因为本质是一个函数没有继承React.Component

react生命周期分为 创建时,更新时,卸载

创建时执行顺序为:

constructor(props){
super(props)
console.log('constructor');
//组件创建时执行 一般在这里做一些初始化操作
}
render(){
console.log('render');
return(
<div>test</div>
)
// 组件每次更新时执行 不要在这里执行this.setState
}
componentDidMount(){
console.log('didmount');
//组件挂载完时执行 一般可在这里操作dom,或网络请求
}

更新时

render(更新时/更新的过程中)->componentDidUpdate(更新完毕)
出发更新的情况:
1 this.setState 2 this.forceUpdate 3 传入新的props

render在数据变化时,重新渲染会调用,componentDidUpdate在更新完后调用,在这里可以操作dom (发送网络请求,更改state)这俩个要加判断,不然会递归 ,一般判断prvprops 是否和当前一样

卸载时

componentWillUnmount(){
    console.log('unmount');
  }
一般在这一阶段做清除工作,比如清除定时器等

高阶组件(render-props模式)

这里的高阶组件定义为:状态管理复用组件
就是封装的组件提供状态,操作状态的方法,但并不提供dom
使用该组件时,需要提供一个函数

class Test2 extends React.Component{
  state = {x:0,y:0}
  handler=(e)=>{
    this.setState({x:e.clientX,y:e.clientY})
  }
  componentDidMount(){
    window.addEventListener("mousemove",this.handler)
  }
  render(){
   return this.props.render(this.state)
  }
}
class Test extends React.Component{
  render(){
    return(
      <div>
        test
        <Test2 render={(mouse)=>{
          return (
          <p>x:{mouse.x}  y:{mouse.y}</p>
          )
        }}></Test2>
      </div>

    )
  }
}
比较难理解  但却是很好用  使用test2时能自己决定dom结构 很灵活
这个方式也可以用children来使用
<Test2>{(mouse)=>{return (<p>x:{mouse.x},y:{mouse.y}</p>)}}</Test2>
render(){
  return this.props.children(this.state)
}

react 高阶组件(HOC)

这个思想类似于将你的组件经过函数处理之后得到另外的组件
例如将test 组件经过封装后得到res组件 res结构是 test的 但res添加上了数据
具体步骤就是:
定义一个函数 参数是组
函数类创建一个类组件,在该组件做state赋值,和管理state ;render里面返回<参数组件 {...this.state} / >
函数最后返回该类组件

import { func } from "prop-types";
import React from "react";
import ReactDOM from "react-dom"

import "./index.css"

function withMouse(WarpC) {
  class Mouse extends React.Component{
    state={x:0,y:0}
    handle=(e)=>{
      this.setState({x:e.clientX,y:e.clientY})
    }
    componentDidMount(){
      window.addEventListener("mousemove",this.handle)
    }
    componentWillUnmount(){
      window.removeEventListener("mousemove",this.handle)
    }
    render(){
      return <WarpC {...this.state}/>
    }

  }
  return Mouse 
}
function Test(props) {
  return(
    <div>{props.x}{props.y}</div>
  )
}

const Res = withMouse(Test)
ReactDOM.render(<Res/>,document.getElementById('root'))

setState

this.setState 是异步更新
count:1
// this.setState({count:this.state.count+20})
console.log(this.state.count);
// this.setState({count:this.state.count+10})
console.log(this.state.count);
上面代码先执行俩个log 输出1,1.之后会执行俩个setState() 最后第二个会覆盖第一个的结果 count:11

通过以下如果想避免这种:
可这种

      console.log(state,props);
      return {
        count:state.count+1
      }
    })
    console.log(this.state.count);
    // this.setState({count:this.state.count+10})
    this.setState((state,props)=>{
      console.log(state,props);
      return {
        count:state.count+1
      }
    })
    console.log(this.state.count);
同样会先执行log 但第一个setState  count:2  ;第二个setState:count:3

this.setState({},()=>{
这里是更改完后,渲染完后执行的回调
})

组件性能优化

react 组件的更新会更新自身和子树组件。这样如果更新root组件,即使子组件没更新也会重新渲染。
因此有必要做些优化

  1. 减少state的体积。state里面只放和渲染有关的变量,其余比如setIntView..的timerID放在组件实例身上即可。
  2. shouldComponentUpdate
    shouldComponentUpdate这个钩子函数在render之前执行,可根据函数返回值来判断是否更新
    shouldComponentUpdate(nextProps,nextState){
    return JSON.stringify(this.state) == JSON.stringify(nextState) ? false : true
    }
  3. 使用纯组件
    原来创建组件使用 extends React.Component 这种方式要我们手动添加shouldComponentUpdate()钩子来优化 ,但 如果是这样 class App extends React.PureComponent这样就自动比较 state , props
    但这里有个坑,在比较引用对象时,比较的是地址,因此修改引用对象时应该新建
    比如:{...{this.state.obj},name:"zql"} [...this.state.arr,{data}]

虚拟DOM 和 diff算法

组件通过render 或直接返回的jsx 其实和React.createElement()效果都是创建的虚拟dom 其本质是一个js对象,有type属性,props属性。每次调用setState都会创建新的虚拟dom,然后通过diff算法对比新旧虚拟dom,找到不同的地方然后在真实Dom打补丁即可

虚拟dom配合diff算法确实可以改善性能 , 但虚拟dom最强大的作用是虚拟dom本质是js也就是说任何支持js代码的地方能运行虚拟dom,这也是react能在服务器端,iOS,Android端跑的原因。摆脱了浏览器的限制,实现跨平台

react路由

首先先下载 npm install react-router-dom
之后如下操作:

import React from "react";
import ReactDOM from "react-dom"
import PropTypes from "prop-types"
import "./index.css"
import {BrowserRouter as Router,Route,Link,Routes} from "react-router-dom"

const Test2 = ()=>{
  return (
    <p>ppp</p>
  )
}

const Test=()=>(
  // 用Router将整个应用包裹起来
  <Router>
    //设置路由入口
    <Link to="/first">first</Link>
    //设置路由出口
    <Routes>
      <Route path="/first" element={<Test2/>}></Route>
    </Routes>
  </Router>
)
ReactDOM.render(<Test />,document.getElementById('root'))

编程式路由 this.props.history.push('/path')
this.props.history.go(-1)

路由版本不同有些不一样
路由默认采用模糊匹配模式(当url中的地址事宜route path 就能匹配)
to=/a/b/c 能被 path=/a命中 to的路径要以path开头才能命中
给 Route组件添加exact属性即可精确匹配

Navlink

import {NavLink} from "react-router-dom"
<NavLink to="/path">path</NavLink>
这种选中就会添加一个className="active"
可以自定义改类名:activeClassName

如果有多个<Route>组件,匹配时会全部匹配,可以使用<Switch>组件,只匹配第一个
重定向;默认;兜底;一般写在所有路由最下方
<Redirect to="/home"/>{/* 默认 */}

向路由组件传参

  1. params 方式
    <Link to={/home/${this.state.id}/${this.state.msg}}>
    <Route path="/home/:id/:msg" component={Test}>
    这样Test组件就可以在this.props.match.params里面拿到数据了
  2. search方式
    <Link to={/home/?id=${this.state.id}&msg=${this.state.msg}}>
    <Route path="home" component={Test}>
    之后Test组件可在this.props.location.search 拿数据
  3. state方式
    <Link to={{pathname:"/home"},state={id:this.state.id,msg:this.state.msg}}>
    <Route path="/home"} component={Test}/>
    Test组件可以在this.props.location.state拿到数据

路由补充

<Link to="/home">aaa<Link/>这种默认是push模式
<Link to="/home" replace>aaa<Link/> replace模式
一般组件的props是没有路由组件props的history等api
将一般组件弄成路由组件可用withRouter函数

import {withRouter} from "react-router-dom"
export default withRouter(App)

编程方路由导航

普通 this.props.history.push("/home") this.props.history.replace("/home")
1.params模式:
this.props.history.push(/home/${id}/${msg})
2.search模式
this.props.history.push(/home/?id=${id}&msg=${msg})
3.state模式
this.props.history.push('/home',{id:id,msg:msg})
this.props.history.go(-1)返回

redux

redux 是集中状态管理的地方,可用在三大框架上面。redux有三个重要组成:action,reducer,state
action:本质是js对象,其中必有type属性;组件通过发送action到store,让store交给reducer加工action,返回state(数据)给store;之后组件可通过store拿到新数据;
store:接收action;判断action是否合法;将action转发reducer;接收reducer返回的数据
reducer:本质是一个函数,参数有preState,action;返回新的state
原理图


官方原理图

尚硅谷redux原理图.png

redux小练习

hooks

有类式组件了,为毛需要函数组件
官方说了三点:

  1. 类组件之间复用状态逻辑困难;需要借助高阶组件<provider><consumer> HOC 或者 render props等;函数组件将状态逻辑抽离出来;对于开发人员很友好
  2. 复杂组件难以理解;在类组件中,每个生命周期函数可干很多事,例如componentdidmmount,componentdidupdate都需要加载数据;那么这是代码复用率低,使用hooks useeffect可解决服用问题
  3. class理解;js中也有class的概念,react class组件,这俩者难免有冲突的地方

我自己理解:class 本就是function 的语法糖;因此使用function理论更快

facebook 推荐使用hooks,但也保留class的维护;其本身也用了很多class 组件;

useState

useState 在函数组件内部调用;产生俩个之,一个是state数据,一个是改变state的函数;可通过该函数改变其值,类似this.setState();区别在于不会想setState合并;
【注】组件会保留该state和操作state的函数 重复渲染时依然用原来的;初始 state 参数只有在第一次渲染时会被用到

import {useState} from "react"
function App() {//
  const [count,setCount] =useState(0) 
//使用useState hook后会产生一个数组,第一个元素为数据,第二个为操作该数据的函数,该函数为异步执行,下面代码会先执行log语句,改变执行顺序可这样写
{setCount(count=>{
count++
log(count)  
return count
})}
  const handle=()=>{
    setCount(count+1) 
    console.log(count);
  }
  return (
    <div>
       count:{count}
       <hr />
       <button onClick={()=>handle() }>count++</button> 
    </div>
  );
}

export default App;


useState((preState)=>{})
useState参数如果是函数,那么该函数的参数就是prev数据,如果该函数没返回值,那么下一次prev数据就为undefined;

reducer

当我们的数据为引用数据时,最好用useReducer

import { useState,useReducer } from "react";

  function App(params) {
    const reducer = (state,action)=>{
      switch (action.type) {
        case "add":
          return {...state,count:state.count+1}
          break;
        case "jian":
          return {...state,count:state.count-1} 
        default:
          return state
          break;
      }
    }
    
    const [state,stateDispatch] = useReducer(reducer,{count:0,name:'zqk'})
    // useReducer执行时,将第二参数传给第一reducer的state作为值,action为undefined,执行后得到一个数组,第一个为返回的值,第二个为操作改值的函数。类似redux。
    const handle=()=>{
      stateDispatch({type:"add"})
    }
    return(
      <div>
        count:{state.count}----name:{state.name}
        <hr />
        
        <button onClick={handle} >count++</button>
      </div>
    ) 
  }


export default App

副作用

副作用就是用到了外部变量就会产生副作用,比如用到了全局变量,就会有修改它的副作用,比如操作dom,网络请求,document.title=''等等

useEffect

场景一

useEffect(()=>{
  console.log('a')
 })
将函数作为参数传入该函数,这个函数就会在组件被挂载后执行,当页面有更新也会执行

场景二

useEffect(()=>{
  console.log('a')
 },[])
将函数作为参数传入该函数,并且将空数组作为第二个参数,这个函数就会在组件被挂载后执行,但是更新时不会执行

场景三

useEffect(()=>{
  console.log('a')
 },[a,b])
将函数作为参数传入该函数,将[a,b]作为第二个参数传入,这个函数就会在组件被挂载后执行,当变量a,b更新也会执行

场景四

useEffect(()=>{
  console.log('a')
  return ()=>{
      console.log('b')
 })
将函数作为参数传入该函数,这个函数就会在组件被挂载后执行,当页面有更新也会执行,该函数返回值是函数,当页面有更新,或者页面被卸载都会执行

场景五

useEffect(()=>{
  console.log('a')
  return ()=>{
      console.log('b')
 },[])
将函数作为参数传入该函数,将[]作为第二个参数,这个函数就会在组件被挂载后执行,当页面有更新不会执行,该函数返回值是函数,当页面有更新不会执行,页面被卸载会执行

场景六

useEffect(()=>{
  console.log('a')
  return ()=>{
      console.log('b')
 },[a])
将函数作为参数传入该函数,将[a]作为第二个参数,这个函数就会在组件被挂载后执行,当a有更新会执行,该函数返回值是函数,当a有更新会执行,页面被卸载会执行

匹配到[a,b...]的更新时,首先会看是否有返回函数,如果过有就先执行它,在执行外函数

函数组件父子传值

父传子和类组件一样
<Test a={1} b={2}/>
function Test(props){...}//接收就使用props

子传父也是类似的
父元素提供函数接口,子组件在合适时候调用函数,将数据通过参数传给父组件。

import { useState, } from "react";
function Test(props) {
  const [name,setName]=useState("dym")
  
  return(
    <div>
      
      test...
      <hr />
      name:{props.name}
      age:{props.age}
      <hr />
      myname:{name}
      <hr />
      !!!!!!!!!
      <button onClick={()=>{props.test('abc')}}>btn</button>
    </div>
  )
}
function App(params) {
  const [count,setCount]=useState(0)
  const [name,setName] =useState("")
  const handle=(msg)=>{
    setName(msg)   
  }
  return(
    <div>
      msg from son :
      count:{count} --myname={name}
      <hr />
      <button onClick={()=>setCount(count+1)}>count++</button>
      <hr />
      <Test name="zql" age="33" test={handle}></Test>
    </div>
  )
}
export default App

useContext

祖孙组件传递用这个

import { useState,useContext ,createContext} from "react";
const Context = createContext() //如果组件不在同一文件,记得将它导出,子孙组件用时,导入即可
function Test(props) {
  return(
    <div>
      test
      <hr />  
      <Test2></Test2>
    </div>
  )
}
// 孙
function Test2(params) {
  const count = useContext(Context)
  return (
    <div>
      test2--count:{count}
    </div>
  )
}
//root 
function App(params) {
  const [count,setCount]=useState(0)
  
  return (
    <div>
      app--count:{count}
      <button onClick={()=>setCount(count+1)}>btn</button>
      <hr />
      <Context.Provider value={count}>
      <Test/>
      </Context.Provider>
      
    </div>
  )
}
export default App
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • react 基本概念解析 react 的组件声明周期 react 高阶组件,context, redux 等高级...
    南航阅读 1,140评论 0 1
  • 1.如果你熟悉 HTML,那么 JSX 对于你来说是没有任何压力的,因为 HTML 中的所有标签,在 JSX 中都...
    落幕_e541阅读 472评论 0 1
  • 关于JSX 考虑这样一段代码:const element = Hello, world! ;这段代码既不是字符串...
    带三本书阅读 456评论 0 1
  • 回顾:根据目前学习内容,更新界面内容的方法为ReactDOM.render() 本次目的:实现clock组件的封装...
    XKolento阅读 395评论 0 1
  • 1.定义组件 1.1函数式组件 1.2类式组件 注意 类式组件没有state, 引入组件要使用大写 组件要大写 执...
    coderlu阅读 292评论 0 0

友情链接更多精彩内容