react学习总结

React 和Vue

React摆脱了传统的JS+CSS+HTML,React=Think in JS+Data in JS+View in JS。React遵循的是函数式编程,react最大的特性(核心思想)就是==组件化==。万物皆可组件化,对于代码的耦合是极大的减小的。

Vue遵循MVVM的设计思想模式,将Data和View独立出,双方只需要关注视图或者数据即可,双方通过viewModel来构成双方沟通的桥梁。model层(数据模型)只需要重数据,view层更多的就是关注界面视图。

组件化

类组件(★★)

通过导入react中的component创建组件。
1.组件类必须继承React.Component类
2.必须要有render方法;
3.render方法的return返回的就是组件的内容

import React, { Component } from 'react';
 class App extends Component {
  constructor() {
    super()
  }
  //render返回渲染DOM
  render() {
    return <h2>Hello App</h2>
  }
}
export default App

函数组件

函数组件是没有生命周期的,
函数组件也是没有内部状态(state),只有props,但是React16.8新增HOOK,使得函数组件中也可以添加state
没有内部的this,打印的是undefined

export default function App(props) {
//没有render()钩子函数,需要通过return来返回结果
  return (
    <div>Hello World</div>
  )
}
<APP sta=“sss” sat2={data}/>

根节点

react和Vue同时都需要一个根节点
所以就需要添加一个 <div ></div>,但是有时候我们在组件中往往不想添加这个div节点,又想要一个根节点,这时候,react中提供了*Reac.Fragement*

render() {
  return (
    <React.Fragment>
      <ChildA />
      <ChildB />
      <ChildC />
    </React.Fragment>
  );
}

Dom渲染时候并不会将Fragement渲染出来
Fragement有简写

 <>
 //内容
  </>

列表 & key

如果不对每个元素设置key属性,就会看到一个警告 a key should be provided for list items ,而且最好用数据中的id作为key值。

function NumberList(props) {
//props接受组件中传过来的属性参数
  const numbers = props.numbers;
  //对于经过便利循环得到的列表,需要给每一个元素分配一个key
  const listItems = numbers.map((numbers) =>
    <li key={number.toString()}>{number}</li>
  );
  return (
    <ul>{listItems}</ul>
  );
}

const numbers = [1, 2, 3, 4, 5];
//render()函数渲染
ReactDOM.render(
//组件列表
  <NumberList numbers={numbers} />,
  document.getElementById('root')
);

Vue

<div v-for="(v,i) in list" key="i" />

state && Props

props 是组件对外的接口,state 是组件对内的接口

state

State是可变的,是一组用于反映组件UI变化的状态集合。相当于Vue中的data函数数据

class State extends React.Component {
  constructor() {
  	super();
  	//初始化state
  	//在组件内部是唯一数据源
    this.state = {data: "string"};
  }
  render() {
    return (
      <div>
        <h2>It is {this.state.data}.</h2>
      </div>
    );
  }
}

如果要改变state中的数据,直接从this.state.data="change string"是无法直接更新vie视图的。
正确的修改方式应该是使用setState()

this.setState({data: 'change string'});

同时 state的更新是异步更新,所以不能依赖当前的State来计算下一个State。

// 错误  依赖的this.state并不能保证是最新的State
this.setState({
  counter: this.state.counter + this.props.increment,
});
// 正确  把值当成参数
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

setState()(★★★)

更新数据

  • setState()更新数据是异步的
  • 注意:使用该语法,后面的setState不要依赖前面setState的值
  • 多次调用setState,只会触发一次render
  • 没有导致state的值发生变化的setState会导致重渲染(render函数重复执行)

推荐语法

  • 推荐:使用 setState((state,props) => {}) 语法

  • 参数state: 表示最新的state

  • 参数props: 表示最新的props

第二个参数

  • 场景:在状态更新(页面完成重新渲染)后立即执行某个操作
  • 语法:setState(update[,callback])

在这里插入图片描述

props(★★★)

  • 组件时封闭的,要接受外部数据应该通过props来实现
  • props的作用:接收传递给组件的数据
  • 传递数据:给组件标签添加属性

而Props对于使用它的组件来说,是只读的,要想修改Props,只能通过该组件的父组件修改。
在组件状态上移的场景中,父组件正是通过子组件的Props, 传递给子组件其所需要的状态。
React中的props与Vue中的props使用方法相似

function Welcome(props) {
//调用props获取到组件传送的属性值
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
    //name属性可以被props.name获取到,name除了普通数据,还可以传函数
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
    </div>
  );
}
//render()函数实现渲染
ReactDOM.render(
  <App />,
  document.getElementById('root')
);

children属性

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

在这里插入图片描述

propTypes(★)

  • 对于组件来说,props是外来的,无法保证组件使用者传入什么格式的数据,简单来说就是组件调用者可能不知道组件封装着需要什么样的数据
  • 如果传入的数据不对,可能会导致报错
  • 关键问题:组件的使用者不知道需要传递什么样的数据
  • props校验:允许在创建组件的时候,指定props的类型、格式等

propTypes可以用来做类型的校验,如果后面有isRequired ,组件中的数据必须要求传递

import PropTypes from 'prop-types'
组件名.propTypes = {
    test: PropTypes.string.isRequired,//isRequired必须传递
    content: PropTypes.string,
    deleteIntem: PropTypes.func,
    index: PropTypes.number
    }

defaultProps

默认值,与上的test属性相结合,test是一个必填的值,当父组件没有传递test值时,则会自动调用defaultProps方法

TodoItem.defaultProps = {
    test: 'hello world'
}

Refs

refs的主要应用场景:

需要直接操作DOM的一些操作:

  • 失去/获取焦点,文本选择或媒体播放
  • 通过DOM完成一些简单动画效果
  • 集成第三方 DOM 库

react中的Refs是使用React.createRef() 创建的,

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    //创建一个空白ref
    this.myRef = React.createRef();
  }
  render() {
  //通过ref属性附加到React中
    return <div ref={this.myRef} />;
  }
}

构造函数中使用 React.createRef() 创建的 ref 接收底层 DOM 元素作为其 current 属性。

所以调用ref时候需要:

  focusTextInput() {
    // 注意:我们通过 "current" 属性来获得DOM节点的焦点
    this.textInput.current.focus();
  }
}

但是React.createRef() 只适合class组件。对于函数组件需要useRef()

function CustomTextInput(props) {
  // 这里必须声明 textInput,这样 ref 才可以引用它
  const textInput = useRef(null);
//通过得到的refs执行DOM操作
  function handleClick() {
    textInput.current.focus();
  }
  return (
    <div>
      <input
        type="text"
        //绑定refs
        ref={textInput} 
        //获取焦点操作
        onClick={handleClick}/>
    </div>
  );
}

Vue

//绑定ref
<input type='text' ref='input1' />
methods:{
    add:function(){
       //this.$refs.input1  获取dom节点
        this.$refs.input1.value ="10"; 
        }
    }

生命周期钩子函数

挂载

创建时候会依次执行constructor(),render(),componentDidMount()
在这里插入图片描述

  • render函数并不做实际的渲染动作,它只负责返回一个JSX描述的结构,最终由React来操作渲染过程。

1. constructor(props):初始化钩子函数

完成React数据初始化,数据已经可以被调用,相当于Vue生命周期中的created

class Clock extends React.Component {
  constructor(props) {
  //完成数据初始化,State和Props中的数据已经可以被访问
    super(props);
    this.state = {date: new Date()};
  }
}

2. componentWillMount()

使用场景较少,在未来react的更新中将会被逐渐废弃。
主要应用场景是在服务端渲染时使用,代表:数据初始化完成,但DOM还未被渲染。相当于Vue中的beforeMounted()

3. componentDidMount()挂载

使用次数最多
组件第一次渲染完成,此时dom节点已经生成,可以在这里调用ajax请求,返回数据setState后组件会重新渲染

class Clock extends React.Component {
  componentDidMount() {
  //挂载
  //执行数据初始化的相关操作,异步请求,定时器设置
  }
}

更新

在这里插入图片描述
在这里插入图片描述

4. shouldComponentUpdate()更新

主要应用场景:
没有导致state的值发生变化的setState会导致重渲染(render函数重复执行)
这会造成操作冗余,影响性能
shouldComponentUpdate函数是重渲染时render()函数调用前被调用的函数,它接受两个参数:nextProps和nextState,分别表示下一个props和下一个state的值。

当函数返回false时候,阻止接下来的render()函数的调用,阻止组件重渲染,而返回true时,组件照常重渲染。

  //在render函数调用前判断:
  //如果前后state中Number不变,通过return false阻止render调用
  shouldComponentUpdate(nextProps,nextState){
      if(nextState.Number == this.state.Number){
        return false
      }
  }

5. componentDidUpdate(prevProps, prevState)

此方法在组件更新后被调用 ,可以操作组件更新的DOM,prevProps和prevState这两个参数指的是组件更新前的props和state。
用于进行判断,数据是否更新成功,防止因数据未更新造成的页面过度刷新操作。

//页面更新数据之后需要调用这个生命周期componentDidUpdate
componentDidUpdate(prevProps, prevState){
//更新后的数据
  let pageSize = this.state.pageSize;
  if(prevProps.pagesize ===pageSize){
     //判断数据是否发生更新,执行相关操作
    }
}

卸载

执行时机:组件从页面中消失

作用:用来做清理操作
在这里插入图片描述

6. componentWillUnmount ()卸载

组件销毁钩子,常用
在此处完成组件的卸载和数据的销毁。
常用于清楚计数器(clearTimeout, clearInterval),移除所有组建中的监听( removeEventListener).

componentWillUnmount() {
    clearInterval(this.timerID);
  }

HOOK

Hook 是一个特殊的函数,它可以让你“钩入” React 的特性。例如,useState 是允许你在 React 函数组件中添加 state 的 Hook

State Hook

在函数组件中,我们没有 this,所以我们不能分配或读取 this.state。我们直接在组件中调用 useState Hook:

import React, { useState } from 'react';
function Example() {
  // 声明一个叫 “count” 的 state 变量
  const [count, setCount] = useState(0);
  return <button onClick={() => setCount(count + 1)}>
    Click me
  </button>
  }

Effect Hook

useEffect可以让函数组件模拟class组件的生命周期
函数组件是一个纯函数,执行完即销毁,自己无法实现生命周期
使用 effect hook 将生命周期“钩”到纯函数中

我们可以把 useEffect Hook 看做 componentDidMountcomponentDidUpdatecomponentWillUnmount 这三个函数的组合。

**useEffect 在默认情况下,它在第一次渲染之后每次更新之后都会执行。React 保证了每次运行 effect 的同时,DOM 都已经更新完毕。

import React, { useState, useEffect } from 'react';
function Example() {
  const [count, setCount] = useState(0);
    const [name, setName] = useState(0);
  useEffect(() => {
    //用来模拟class组件中的componentDidMount,componentDidUpdate和componentWillUnMount
    console.log('数据初始化渲染和更新数据在这里实现')
    //相当于生命周期中的componentWillUnmount钩子函数
     return() => {
	//清除定时器
		window.clearInterval(timeinter)
	}

  },[count, name]);
      
  });
  return (
    <div>
      <button onClick={()=> setCount(count+ 1)}>Click</button>
    </div>
  );
}

此时相当于componentDidMount + componentDidUpdate钩子的组合,初次渲染并且状态改变时都会触发

useEffect( ()=>{
 console.log('数据初始化渲染会执行')
 console.log('数据发生更新也会执行该函数')
 } )

我们传入 [count] 作为第二个参数,如果 count 的值是 5,而且我们的组件重渲染的时候 count 还是等于 5,React 将对前一次渲染的 [5] 和后一次渲染的 [5] 进行比较。因为数组中的所有元素都是相等的(5 === 5),React 会跳过这个 effect,这就实现了性能的优化。

useEffect(()=>{
   console.log('count值发生变化就会执行该effect')
   console.log('count值不变,数据更新后就跳过执行这个effect')
},[count])

如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。这就告诉 React 你的 effect 不依赖于 props 或 state 中的任何值,所以它永远都不需要重复执行。这并不属于特殊情况 —— 它依然遵循依赖数组的工作方式。

useEffect(() => {
console.log("只在初次componentDidMount时执行一次")
}, []);

useEffect中返回一个函数,这个函数相当于componentWillUnmount 钩子, 卸载组件的时候触发

useEffect(()=>{ 
	console.log('数据初始化渲染会执行')
 	console.log('数据发生更新也会执行该函数')
	return ()=>{
    	console.log("组件将要被卸载了")
  	} 
 })

组件传值

父组件向子组件传值

父组件在子组件中添加了属性,传入到child组件中的props的count属性

class Father extends React.Component {
    constructor(props){
    	super(props)
        // 提供共享的状态
        state = {
            count: 0
        }
    }
    render() {
        return (<div>
            {/* 把状态提供给第一个子组件 */}
            <Child count={this.state.count}/>
        </div>
        )
    }
}

子组件通过this.props获取到渲染组件传入的属性参数

class Child extends React.Component {
    constructor(props){
    super(props)
    }
    render() {
        return (
            <h1>计数器:{this.props.count}</h1>
        )
    }
}

子组件向父组件传值

  • 利用回调函数,父组件提供回调,子组件调用,将要传递的数据作为回调函数的参数
  • 父组件提供一个回调函数,用来接收数据
  • 将该函数作为属性的值,传递给子组件
    class Parent extends React.Component{
        getChildMsg = (msg) => {
          console.log('接受到的子组件数据',msg);
        }
        render(){
            return(
                <>
                <Child getMsg={this.getChildMsg}/>
                </>
            )
        }
    }

子组件通过props返回数据

    class Child extends React.Component{
        state={value:'childMsg'}
        handleClick = () => {
          this.props.getMsg(this.state.value)
        }
        render(){
            return(
                <>
                <button OnClick={this.handleClick}></button>
                </>
            )
        }
    }

兄弟之间共享数据

class Counter extends React.Component {
    // 提供共享的状态
    state = {
        count: 0
    }
    // 提供共享方法
    onIncrement = (res) => {
        // 只要第二个子组件调用了这个函数,就会执行里面代码
        this.setState({
            count: this.state.count + res
        })
    }
    render() {
        return (<div>
            {/* 把状态提供给第一个子组件 */}
            <Child1 count={this.state.count}/>
            {/* 把共享方法提供给第二个子组件 */}
            <Child2 onIncrement={this.onIncrement} />
        </div>
        )
    }
}

Child1

  • 在第一个子组件里面就能通过props获取到
class Child1 extends React.Component {
    render() {
        return (
            <h1>计数器:{this.props.count}</h1>
        )
    }
}

Child2

  • 在父组件中提供共享方法,通过属性传递给第二个子组件,方便第二个子组件来进行调用
class Child2 extends React.Component {
    handleClick = () => {
        // 这里一旦调用,就会执行父组件里面 onIncrement函数
        this.props.onIncrement(2)
    }
    render() {
        return (
            <button onClick={this.handleClick}>+</button>
        )
    }
}

Context(★★)

  • 如果两个组件相隔层级比较多,可以使用Context实现组件通讯
  • Context提供了两个组件:Provider 和 Consumer
  • Provider组件: 用来提供数据
  • Consumer组件: 用来消费数据
    应用场景: 跨组件传输数据,多适用于父->子->孙的嵌套传值
  • 调用 React.createContext() 创建 Provider(提供数据) 和 Consumer(消费数据) 两个组件
const {Provider, Consumer} = React.createContext()
//通过Provider传值
<Provider value="dark">
//Toobar 中的子组件就不需要不停的传递props
   <Toolbar />
 </Provider>
  • 哪一层想要接收数据,就用Consumer进行包裹,在里面回调函数中的参数就是传递过来的值
 render() {
    return <Button theme={this.context} />;
  }
ThemedButton.contextType = ThemeContext;
//or 第二种写法
<Consumer>
 Consumer的children必须是一个函数,
//传递的等于组件树中层这个 context 最接近的 Provider 的对应属性
   {theme =><Button theme={theme} />; // 核心代码}
</Consumer>

高阶组件(★★★)

组件是对一些功能进行封装,而高阶组件则是对组件的一种包装,加强组件的功能。相当于手机壳是对手机的一种包装。

// 定义一个函数,在函数内部创建一个相应类组件
//普通函数,参数数量自定义
function withMouse(WrappedComponent,data) {
    // 该组件提供复用状态逻辑
    class Mouse extends React.Component {
        state = {
            x: 0,
            y: 0
        }
        // 事件的处理函数
        handleMouseMove = (e) => {
            this.setState({
                x: e.clientX,
                y: e.clientY
            })
        }
        // 当组件挂载的时候进行事件绑定
        componentDidMount() {
            window.addEventListener('mousemove', this.handleMouseMove)
        }
        // 当组件移除时候解绑事件
        componentWillUnmount() {
            window.removeEventListener('mousemove', this.handleMouseMove)
        }
        render() {
            // 在render函数里面返回传递过来的组件,把当前组件的状态设置进去
            return <WrappedComponent {...this.state} />
        }
    }
    return Mouse
}

哪个组件需要加强,通过调用withMouse这个函数,然后把返回的值设置到父组件中即可

function Position(props) {
    return (
        <p>
            X:{props.x}
            Y:{props.y}
        </p>
    )
}
// 把position 组件来进行包装
let MousePosition = withMouse(Position)

class App extends React.Component {
    constructor(props) {
        super(props)
    }
    render() {
        return (
            <div>
                高阶组件
                <MousePosition></MousePosition>
            </div>
        )
    }
}

render Props(★★)

render prop 是一个用于告知组件需要渲染什么内容的函数 prop。

class Cat extends React.Component {
  render() {
    const mouse = this.props.mouse;
    return (
      <img src="/cat.jpg" style={{ position: 'absolute', left: mouse.x, top: mouse.y }} />
    );
  }
}

class Mouse extends React.Component {
  constructor(props) {
    super(props);
    this.state={x:123,y:225}
  }
  render() {
    return (
    //公共的渲染部分
      <div style={{ height: '100vh' }}>
		/*
		*使用“render”道具来动态决定渲染什么
		*/
        {this.props.render(this.state)}
      </div>
    );
  }
}

class MouseTracker extends React.Component {
  render() {
    return (
      <div>
        <h1>render Props 渲染</h1>
        <Mouse render={mouse => (
          <Cat mouse={mouse} />
        )}/>
      </div>
    );
  }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值