(Day1) React + TypeScript 复习总结

编程入门 行业动态 更新时间:2024-10-14 06:24:41

(Day1) <a href=https://www.elefans.com/category/jswz/34/1771383.html style=React + TypeScript 复习总结"/>

(Day1) React + TypeScript 复习总结

JSX

  1. JSX的本质是JS对象:JSX标签对象 ------> 映射到html标签对象中,React就是用这种方式将JSX渲染到html文档中。
  2. 简单描述一下JSX:
    jsx就是js于xml的一种结合体,理解为js于xml可以一起使用。它在运行的时候会被babel转译 生成 执行React.createElement(‘h1’,null,‘hello’)返回生成的对象结构(React元素),根据这个结构,最后进行页面的渲染(React.render())。
  3. jsx的属性 / 表达式:在大括号中,放一些变量,表达式;属性方面 className , style等。
  4. jsx可以在循环中使用。
import React from 'react';
import ReactDOM from 'react-dom';
let root: HTMLElement | null = document.getElementById('root');let arr:Array<string> = ['uzi','ming','xiaohu'];
let ReactAry:Array<React.ReactElement> = [];
for( let i = 0;i < arr.length ; i++ ){ReactAry.push(<li key={i}>{arr[i]}</li>);
}ReactDOM.render(ReactAry,root); 

React元素的更新

React元素被创建之后就不可变了,要更新的话只能创建新的元素,重新渲染,并且只会更新必要的部分(DOMdiff 前后比较)。

import React from 'react';
import ReactDOM from 'react-dom'
let root: HTMLElement | null = document.getElementById('root');let fn = function(){let ele: React.ReactElement = (<div><div>更新时间:</div><div>{new Date().toLocaleTimeString()}</div></div>)ReactDOM.render(ele,root)
}setInterval(()=>{fn()
},1000)

React组件 函数式组件和类组件

注意:我们除了学习基本用法外,还要明白它们之间的优缺点,不同之处。

扩展 1) 纯函数: 没有改变自己输入的值,并且也不改变作用域外的变量。

function add(a: number,b:number){ //纯函数return a + b
}
function withdraw(account: {name: string},num:number){ //不是纯函数account.name += 'adc';global.name = 10
}
withdraw({name:'uzi'},10)
  • React是单向数据流,数据只能从上往下传递,并且props是不能修改的。
  • 函数组件就像是一个纯函数,因为这个函数接收了属性对象,而且是只读的.然后返回一个值,也就是:输入是确定的,输出就是确定的。
  • 不过 react,vue都要剥离class组件的趋势,改用函数值组件 hooks => 类组件有的功能,函数式组件也都有。

扩展 2)函数式组件与类组件的区别
1. 函数式组件的性能比类组件的性能要高。毕竟函数只需要执行返回对应的React元素即可,并且每次运行完成后会销毁,减少了内存的开销,而类组件还需要实例化。
2. 函数式组件 没有 this状态生命周期 ;类组件有this、有生命周期有状态state

函数组件的渲染类组件的渲染
1. 收集props对象;
2.把属性对象传入函数并返回React元素;
3.把React元素渲染到页面上。
1. 也是收集props对象;
2. 实例化对应类的实例;
3. 调用实例的render方法,获得返回的React元素;
4. 把返回的元素渲染到界面上就行。

复合式组件

和普通的复合写法一样,就是注意一下 类型 即可。

import React from 'react';
import ReactDOM from 'react-dom'let root: HTMLElement | null = document.getElementById('root');
let style: React.CSSProperties = { // 规范css样式的类,乱写的话会报错。border: '1px solid red',padding:5,
}
interface UpData { // 在定义的时候要使用泛型(接口),使用的时候要指定具体props中值的类型.(调用的是时候要使用真实的接口类型.)name1: string;name2: string
}
interface RngData {uzi: string
}
interface IgData {theshy: string
}class RNG extends React.Component<RngData> { //  这里的泛型 修饰的是 this.props (传递过来的参数)render(): React.ReactElement{return (<div>我是RNG电子竞技俱乐部的{this.props.uzi}</div>)}
}class IG extends React.Component<IgData> {render(): React.ReactElement{return (<div>我是IG电子竞技俱乐部的{this.props.theshy}</div>)}
}class LPL extends React.Component<UpData>{render(): React.ReactElement {// this.props.headString = '12' // props是不可修改的let {name1,name2} = this.propsreturn (<div style={style}><RNG uzi={name1}/><IG theshy={name2}/></div>)}
}let data: UpData = {name1: 'uzi',name2: 'TheShy'
}
ReactDOM.render(<LPL {...data} />,root)

如何定义默认属性(结合类型检查)

props 设置默认属性: static defaultProps = {…} ;
类型检查:static propTypes = {…} 或者 类.propTypes = {…}

import React from 'react';
import ReactDom from 'react-dom';
import PropTypes from 'prop-types'
let root: HTMLElement | null = document.getElementById('root');interface DataProps {name?: string;gender?: 'male' | 'female';hobby?: Array<string>;age?: number;position?: { x: number, y: number };[propName: string]: any // extends Record<string,any>   29行报错解决方案
}class Animal extends React.Component<DataProps> {static defaultProps:DataProps  = { // static defaultProps = {} 给props添加默认属性。name: 'jacklove'}// static propTypes:Record<string,any>static propTypes = { // 对props中的属性进行类型检查 <=> Animal.propTypes = {...}name: PropTypes.string.isRequired,gender: PropTypes.oneOf(['male','female']).isRequired,hobby: PropTypes.arrayOf(PropTypes.string), // 存字符串类型的数组position: PropTypes.shape({x: PropTypes.number,y: PropTypes.number}),// 自定义校验属性 校验年龄age必须大于0并且小于100岁age(props: DataProps, propName: string, componentName: string){let age = props[propName]; //29行 因为根本不知道 propName里面是值,所以就要求PersonProps类型要支持索引属性。if(age < 0 || age >100){return new Error(`Invalid Prop ${propName} supplied to ${componentName}`);}return null;}}render() {let { name,gender,hobby,age,position } = this.propsreturn (<div><ul><li>name: {name}</li><li>gender: {gender}</li><li>hobby: {hobby}</li><li>age: {age}</li><li>position: {position!.toString()}</li></ul></div>)}
}let dataProps: DataProps = {gender: 'male',hobby: ['uzi', 'xiaohu'],age: 29,position: {x: 789,y: 9}
}ReactDom.render(<Animal {...dataProps} />, root);

原理的基本实现

createElement 创建虚拟DOM

众所周知,React中标签元素会在Bable中进行转移,生成执行函数React.createElement(“h1”, null, “Hello”); 的结果。

// 页面中
<h1 className='rng' style={{color: 'red',fontSize: '25px' }}>Hello</h1>完全等价于 
React.createElement("h1", {className: "rng",style: {color: 'red',fontSize: '25px'}
}, "Hello");
// React.createElement的实现
// 思路:就是往对象中添加各个需要的属性,作为React元素返回。// createElement.js
export class Component<P = any> { // 类组件类型(类也是对象,所以也可以作为接口使用)isComponent = true; // 类组件的标志public props: P;static isComponent = true;constructor(props: P){this.props = props}
}export interface FunctionConstructor { // 函数组件类型// (props) => HTMLElement
}export interface ReactElement {type: string | FunctionConstructor | Component,props: any
};function createElement(type: string | FunctionConstructor | Component<any>,config: Record<string,any>,...children:Array<any>): ReactElement {// type的类型有 字符串 、 函数(组件)、类(组件)let props: Record<string,any> = {}for (const propName in config) {props[propName] = config[propName]};props.children = children;let element: ReactElement = {type,props};return element
}
export default createElement;// index.js
let ele = createElement("h1", {className: "rng",style: {color: 'red',fontSize: '25px'}
}, "Hello")console.log(ele)  // {type: "h1", props: {…}}

render渲染

// element = {type:'h1',props:{className,style,children}}
// 思路: 根据第一个参数传递的类型不同,分布进行处理,根据生成type生成对应的标签。再对标签进行属性的添加,最后渲染到页面。// render.js
function render(element: ReactElement,container: HTMLElement): any{if( typeof element === 'string'){return container.appendChild( document.createTextNode(element) )};let type = element.type,props = element.props;let domElement: HTMLElement;if( (type as Component ).isComponent ){ // 传入的是类element = new (type as any)(props).render();type = element.type;props = element.props;domElement = document.createElement(type as string);}else if( typeof type === 'function' ){element = type();type = element.type;props = element.props;domElement = document.createElement(type as string);}else {domElement = document.createElement(type as string);}for (const propName in props) {if(propName === 'className'){domElement[propName] = props[propName]}else if(propName === 'style'){let styleObject: CSSStyleDeclaration = props[propName];for (const attr in styleObject) {domElement.style[attr] = styleObject[attr];}}else if( propName === 'children' ){props[propName].forEach( (child: any) => {render(child,domElement);} )}else{domElement.setAttribute(propName,props[propName]);}}container.appendChild(domElement)
}
export default render// index.js
let ele = createElement("h1", {className: "rng",style: {color: 'red',fontSize: '25px'} }, "Hello")render(ele, root!);

函数组件的实现

interface Props {title: string
}
let Dog: FunctionConstructor = (props: Props) => {// return <h1 className='rng' style={{color: 'red',fontSize: '25px' }}>Hello</h1>return createElement("h1", {className: "rng",style: {color: 'red',fontSize: '25px'}}, "Hello")
}
let element = createElement(Dog,{title: '标题'})render(element,root as HTMLElement)

类组件的实现

// 类组件
interface Props {title: string
}
class Dog extends Component<Props> {constructor(data: any) {super(data)}render() {return createElement('h1', { className: 'ok', style }, 'uzi', 'xiaohu')}
}
let element = createElement(Dog , { title: '标题' })
render(element,root as HTMLElement);

状态

状态的用法

. 状态的接受:class Axx extends React.Component <Props,State>
Props,State 分别为 props参数 与 state 状态的 类型接口。
扩展: setInterval 定时器类型的设定 ----> timerID: NodeJS.Timer | null = null

// 状态的用法// setState 可以看收藏的文章。
import React from 'react';
import ReactDOM from 'react-dom'
let root:HTMLElement | null = document.getElementById('root')interface Props {}interface State {number: number
}class Animal extends React.Component<Props,State> { // 两个状态// timerID: number|null = null // timerID: number|null = null 报错public timerID: NodeJS.Timer | null = null;constructor(props: Props){super(props);this.state = { // 当前状态number: 0}}componentDidMount(){ // 组件挂载完成后,会执行此生命周期函数this.timerID = setInterval(()=>{this.setState({ // 设置新的状态,设置完新的状态后会引起界面的更新。number: this.state.number + 1})},1000)// clearInterval(this.timerID) // 如果参数出现了问题,需要强行转为number类型。}componentWillMount(){clearInterval(Number(this.timerID)) // this.timerID!}render(){return (<div>{this.state.number}</div>)}
}ReactDOM.render(<Animal/>,root as HTMLElement);

setTimeout 异步 更新状态

  1. 在jsx中函数执行的三种写法,以及最优写法
  2. setState的更新的方式
// setTimeout更新状态import React from 'react';
import ReactDOM from 'react-dom'
let root:HTMLElement | null = document.getElementById('root')interface Props {}interface State {number: number
}class Animal extends React.Component<Props,State> { // 两个状态state = {number: 0};// 箭头函数可以保证函数中的this永远指向类的实例。// setState的更新可能是异步的,多个setState可能会被合并为一个。handerClick = (ev: React.MouseEvent) => {// 在事件处理函数中调用setState的时候,this.state的状态没有真正改变。(setState是异步的)// 如果我想从上一个状态计算一个状态的话,需要传递一个函数而非对象。/*  // 传递函数式 => 不会被压缩 this.setState((state) => ({ // 函数传递的第一个参数就是‘旧的’statenumber: state.number + 1}))this.setState((state) => ({number: state.number + 1}))this.setState((state) => ({number: state.number + 1}))*/this.setState({number: this.state.number + 1})console.log(this.state.number) // 因为this.state不会立即改变 =》 0this.setState({number: this.state.number + 1})console.log(this.state.number) // 同上 =》 0/*压缩 后console.log(this.state.number);console.log(this.state.number);this.setState({number: this.state.number + 1})*/// setTimeout里的代码比较特殊,不会走批量更新,会立即执行跟新。// =》 setState 在原生事件和setTimeout 中都是同步的// 上面的执行完为 state => 1setTimeout(()=>{this.setState({number: this.state.number + 1})console.log(this.state.number) // 2this.setState({number: this.state.number + 1})console.log(this.state.number) // 3},0)}render(){return (<div><p>{this.state.number}</p><button onClick={this.handerClick}>+</button></div>)}
}ReactDOM.render(<Animal/>,root as HTMLElement);

状态更新可能会合并

import React from 'react';
import ReactDOM from 'react-dom'
let root:HTMLElement | null = document.getElementById('root')interface Props {}interface State {number: number;name: string;age?: number;
}class Animal extends React.Component<Props,State> { state = {number: 0,name: 'uzi'};handerClick = (ev: React.MouseEvent) => {// 这里的状态可能只是一部分状态,这种部分状态会被合并到总的状态上去。// 新的会覆盖旧的,原来没有的 会追加上去。this.setState({number: this.state.number + 1,age: 90})}render(){return (<div><p>{this.state.name}:{this.state.number}</p><button onClick={this.handerClick}>+</button></div>)}
}ReactDOM.render(<Animal/>,root as HTMLElement);

单向数据流、状态提升

// 单向数据流、状态提升import React from 'react';
import ReactDOM from 'react-dom'
let root:HTMLElement | null = document.getElementById('root')interface Props {club: string,
}interface State {number: number;name: string;
}class Animal extends React.Component<Props,State> { state = {number: 0,name: 'uzi'};// HTMLButtonElement 是 button 的事件类型。(当前事件源DOM类型,使得ev更加具体);// MouseEvent 是浏览器DOM事件类型,与 React.MouseEvent 是不同的。handerClick = (ev: React.MouseEvent<HTMLButtonElement,MouseEvent>) => { // console.log(ev.target) // 事件源console.log(ev.currentTarget.id); //获取事件源属性 要使用 ev.currentTarget 来获取。this.setState({number: this.state.number + 1});}add = () => {this.setState({number: this.state.number + 1})}render(){return (<div><p>{this.state.name}:{this.state.number}</p><button id='jklove' onClick={this.handerClick}>+</button><Dog {...this.props} {...this.state} add={this.add}/></div>)}
}
// 父组件传递下来的值,都会记录在子类的props中。
// 子类不可以修改父组件传递下来的值,是只读的。
// 虽然子类不能动父类的属性和状态,但是 子类可以执行父类传递过来的方法 => 从而修改父类的属性 => 状态提升
// 状态提升:两个组件要共享数据,将数据放在他们共有的最近的父类上
class Dog extends React.Component<Props & State & {add: any} >{ // 将State中的状态合并到Props中。render(){return (<div>{this.props.club} : {this.props.name} : {this.props.number}<button onClick={this.props.add}>++</button></div>)}
}// 函数组件
type SubConterProps = Props & State
// React.SFC === React.FunctionComponent 就是指函数组件。
// 将React.SFC<PropsType>的类型分配给变量,以便将其理解为React无状态功能组件。
let SubConter: React.SFC<SubConterProps> = function(props: SubConterProps){return (<div>{props.club} : {props.name} : {props.number}</div>)
}let props:Props & State = {club: 'RNG',name: 'uzi',number: 12
}
ReactDOM.render(<Animal club='ig'/>,root as HTMLElement);
// ReactDOM.render(<SubConter {...props} />,root as HTMLElement);

新生命周期函数

少了创建时的 ComponentWillMount;
多了更新时的 getDerivedStateFromProps、getSnapshotBeforeUpdata;

// getDerivedStateFromProps 例子
import React from 'react';
import ReactDOM from 'react-dom'
let root:HTMLElement | null = document.getElementById('root')interface Props {}interface State {number: number
}class Counter extends React.Component<Props,State> {constructor(props: Props){super(props);this.state = {number: 0}}handerClick = () => {this.setState({number: this.state.number + 1})}render(){return (<div><p>{this.state.number}</p><button onClick={this.handerClick}>+</button><hr/><ChildCounter n={this.state.number}/></div>)}
}interface ChildCounterProps {n: number
}
interface ChildCounterState {num: number
}
class ChildCounter extends React.Component<ChildCounterProps,ChildCounterState> {state = {num: 0};// 把属性映射为新的状态,它返回的就是新的状态对象。// componentWillReceiveProps 已经废弃,取而代之的是 getDerivedStateFromProps; 静态要比非静态的性能要高一些。static getDerivedStateFromProps(nextProps: ChildCounterProps,prevState: ChildCounterState){const {n} = nextProps;if( n%2 === 0 ){return {num: n*2};}else{return {num: n*3}}}shouldComponentUpdate(): boolean{return this.state.num % 2 === 0 // 如果 值为true 则进行更新。}// 补充:// render是一个函数,它把react组件转成虚拟DOM元素;// react-dom负责把虚拟DOM变成真实DOM.render(){return (<div>{this.state.num}</div>)}
}ReactDOM.render(<Counter />,root as HTMLElement);
// getSnapshotBeforeUpdata 例子
// 新生命周期 getSnapshotBeforeUpdate 实现 页面固定显示位置
import React,{RefObject} from 'react';
import ReactDOM from 'react-dom'
let root:HTMLElement | null = document.getElementById('root')interface ScrollProps {}interface ScrollState {messages: Array<string>
}class ScrollList extends React.Component<ScrollProps,ScrollState> {timer: NodeJS.Timeout | null = nullwrapper: RefObject<HTMLDivElement>; // 真实dom元素的类型constructor(props: ScrollProps){super(props);this.state = {messages: []}this.wrapper = React.createRef<HTMLDivElement>(); // ???????}componentDidMount(){this.timer = setInterval(()=>{this.setState({messages: [`messages-${this.state.messages.length}`,...this.state.messages]})},1000)}componentWillUnmount(){clearInterval(Number(this.timer));}// getSnapshotBeforeUpdate 可以获取 老的 DOM元素的信息,将返回值,以参数的形式传递到componentDidUpdate的第三个参数中。getSnapshotBeforeUpdate(): number{// this.wrapper.current < = > HTMLDivElementreturn this.wrapper.current!.scrollHeight // 内容高度}componentDidUpdate(prevProps: ScrollProps, prevState: ScrollState , prevScrollHeight: number){this.wrapper.current!.scrollTop = this.wrapper.current!.scrollTop + (this.wrapper.current!.scrollHeight - prevScrollHeight);}render(){let style = {height: 100,width: 200,border: '1px solid red',overflow: 'auto'}return (<div style={style} ref={this.wrapper}>{this.state.messages.map((message: string,index: number) => (<div key={index}>{message}</div>) )}</div>)}
}ReactDOM.render(<ScrollList />,root as HTMLElement);

更多推荐

(Day1) React + TypeScript 复习总结

本文发布于:2024-03-23 22:26:07,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1743552.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:React   TypeScript

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!