如何响应HTTP服务:
- 先载入一个index.html
- Index 引入 bundle js
- Codesandbox 将要运行的js 打包放到 bundle js
- Bundle js 载入客户端
- Bundle js 开始执行
React如何被执行
一个负责协调工作
一个负责展示
定义全局数据变量,并让某些对象的展示随着他的变化而变化
目录结构和用处
最新版本的js文件被babel翻译为通用的js
A deep dive into React Fiber - LogRocket Blog介绍react fiber如何工作的
https://blog.logrocket.com/deep-dive-react-fiber/
The image below shows that each fiber node is composed of four phases required to complete that unit of work.
It’s important to note here that each node doesn’t move to completeUnitOfWork() until its children and siblings return completeWork().
For instance, it starts with performUnitOfWork() and beginWork() for <App/>, then moves on to performUnitOfWork() and beginWork() for Parent1, and so on. It comes back and completes the work on <App> once all the children of <App/> complete work.
This is when React completes its render phase. The tree that’s newly built based on the click() update is called the workInProgress tree. This is basically the draft tree waiting to be rendered.
3个重要的组件概念
组件设计的步骤
Props/Properties的作用:父节点给子节点传值
Props 如何传给child
组件的相互引用
import React from "react";
import ReactDOM from "react-dom";
import CommentDetail from "./CommentDetail";
平级(兄弟)组件之间传递数据,或者从下级组件向上级组件传递数据,你可能需要使用状态管理库(如Redux或MobX)或者使用上下文(Context API)
将子组件作为props传递给父组件
之前的Function 和 class的区别
现在的
Class组件存在早期项目中,新的react项目中class 和 function 组件都可能存在
类组件的要求
State初始化和更新方法:
组件的生命周期,render函数会被调用很多次,一旦组件需要被更新就会调用
this is render
index.js:17 my component was rendered to the screen (didMount)
index.js:25 this is render
index.js:21 my component was updated to the screen
3个不怎么被使用的组件生命周期事件方法
const seasonConfig ={
summer:{
text:"Let's hit the beach",
iconName:'sun'
},
winter:{
text:'Burr it\'s chilly',
iconName:'snowflake'
}
}
#用对象给多个值同时赋值
const {text, iconName} = seasonConfig[season]
在component中引入的css在编译后会被放入index
import './SeasonDisplay.css'
设置props的默认值,这样如果props如果没有传,就可以使用这里面的值了
Spinner.defaultProps ={
message:"Loading..."
}
在render中调用其他方法,分离构造的步骤,实现整体和局部的区分
使用类组件的好处
setState 只会更新出入的值,不传入的值不会变化
带括号的每次render都会被调用
不带括号用户输入时候才能被触发
Controlled element把数据放在组件内部。
通过箭头函数来触发事件,可以默认将this和当前变量进行绑定,不会出现找不到 onFromSubmit的情况
Props只能由父节点向子节点传递
父节点定义方法通过props传入
onSearchSumit(term) {
console.log(term)
}
render(){
return(<div className="ui container" style={{marginTop:'10px'}}>
<SearchBar onSubmit={this.onSearchSumit}/>
</div>)
}
子节点调用方法
onFormSubmit=event=>{
event.preventDefault();
// console.log(this.state.term)
this.props.onSubmit(this.state.term)
}
render(){
return(
<div className="ui segment">
<form className="ui form" onSubmit={this.onFormSubmit}>
父子节点事件的定义,传入和返回
const ImageList = (props) =>{
const images = props.images.map((image)=>{
return <img src={image.urls.regular} ></img>
})
return <div>{images}</div>
}
使用map方法来循环构建显示内容
如何通过Ref来访问当前元素的属性,current.addEventListener('load' 这个监听可以在元素载入后执行setSpans方法
class ImageCard extends React.Component{
constructor(props){
super(props)
this.imageRef = React.createRef()
}
componentDidMount(){
this.imageRef.current.addEventListener('load',this.setSpans)
console.log(this.imageRef)
}
setSpans=()=>{
console.log(this.imageRef.current.clientHeight)
}
render(){
const {description,urls}=this.props.image;
return(
<div>
<img
ref={this.imageRef}
alt={description}
src={urls.regular}
/>
</div>
)
}
}
在style中调用state的变量
<div style={{gridRowEnd: `span $(this.state.spans)`}}>
通过Fragment来包装返回的内容,在渲染中Fragment不会被展示在页面上。
const renderItems = items.map((item,index) =>{
return <React.Fragment key={item.title}>
<div className="title active" onClick={()=>console.log('clicked',index)}>
<i className="dropdown icon"></i>
{item.title}
</div>
<div className="content active">
{item.content}
</div>
</React.Fragment>
}
在map函数中,可以传入index表示元素序列
在方法类中使用state ref 和生命周期事件。
在React中,类组件和函数组件在很多方面都有一些差别。以下是它们之间的一些主要区别:
-
声明方式:
- 类组件:使用class关键字定义,然后使用React.Component继承。
- 函数组件:是一个简单的JavaScript函数。
-
状态管理:
- 类组件:可以使用this.state来直接访问和修改状态。
- 函数组件:必须通过使用React的Hook(如useState和useReducer)来管理状态。
-
生命周期方法:
- 类组件:有专门的生命周期方法,例如componentDidMount,componentDidUpdate等。
- 函数组件:没有这些生命周期方法,但可以通过useEffect Hook来模拟它们。
-
渲染性能:
- 类组件:每次渲染时,都会执行componentDidMount,componentDidUpdate等生命周期方法。
- 函数组件:在每次渲染时,不会调用额外的函数,因此它可能在性能方面有一些优势。
-
传入的props更新:
- 类组件:在父组件更新时,子组件的生命周期方法可能会被调用,即使传入的props没有变化。
- 函数组件:当传入的props没有变化时,不会触发新的渲染。
-
上下文(context):
- 类组件:可以通过this.context访问上下文。
- 函数组件:必须手动传递上下文,不能直接访问。
-
类名和函数名冲突:
- 类组件:由于使用类继承,可能会遇到类名冲突的问题。
- 函数组件:不会存在这个问题,因为函数名是唯一的。
-
可测试性和可维护性:
- 类组件:在某些情况下可能更难测试和重构。
- 函数组件:通常更易于测试和维护,因为它们是纯函数,不依赖于任何外部状态或生命周期方法。
-
兼容性:
- 类组件:可以与较旧的浏览器和React版本兼容。
- 函数组件:需要React 16.8或更高版本才能使用。
-
与Hooks的互操作性:
- 类组件:不能直接使用Hook,必须通过高阶组件或其他方式将其引入。
- 函数组件:可以直接使用Hook。
-
在代码组织中的使用:
- 类组件:通常用于有复杂逻辑或需要维护状态的较大组件。
- 函数组件:通常用于简单的无状态组件或高阶组件。
-
与Redux等状态管理库的互操作性:
- 类组件:与Redux等库的互操作性更好,因为它们有明确的生命周期方法和props更新机制。
- 函数组件:可能需要额外的逻辑来处理与Redux的交互,特别是当需要分发action并等待reducer更新state时。
-
在大型项目中的使用:
- 类组件:在大型项目中,类组件可能更容易管理和维护,因为它们有更明确的生命周期和状态管理方法。
- 函数组件:在大型项目中可能更难以管理和维护,特别是当有大量的无状态函数组件时。在这种情况下,可能需要使用一些工具或模式(如高阶组件或渲染 props)来更好地组织代码。
函数组件中状态的初始化
const Accordion = ({items}) =>{
const [activeIndex, setActiveIndex] = useState(null);
与类组件的对比
类组件可以一次更新多个state变量,函数组件只能多次调用setXXX来更新:
使用useEffect的场景:
不同写法对应1-3的场景
在useEffect中调用异步函数
useEffect( ()=>{
const search = async ()=>{
await axios('https://en.wikipedia.org/w/api.php?origin=*&action=opensearch&search=aia')
}
search()
},[term])
import React,{useEffect, useState} from "react";
import axios from 'axios'
import { keyboard } from "@testing-library/user-event/dist/keyboard";
const Search =()=>{
const [term,setTerm] = useState('')
useEffect( ()=>{
const search = async ()=>{
await axios.get('https://en.wikipedia.org/w/api.php',{
params:{
action:'query',
list:'search',
origin:'*',
format:'json',
search:term,
}
})
}
search()
},[term])
return (
<div>
<div className="ui form">
<div className="field">
<label>Enter Search Term</label>
<input value={term} onChange={e=>setTerm(e.target.value)} className="input"/>
</div>
</div>
</div>)
}
export default Search
Hook的使用,useState
是用来帮助创建和更新组件的状态的(把钩子挂上,并传导动力),而 useEffect
是用来执行副作用操作的(接受动力后触发的动作)。
在useEffect中调用顺序:先执行 useEffect 内的,再返回 return 方法(还不执行)
下次use Effect被调用时候,return方法被执行,再执行useEffect内的内容,再返还return方法(暂不执行) 如此循环
示例代码:
useEffect( ()=>{
console.log ('first render or term changed')
return()=>{
console.log('clean up')
}
},[term])
执行结果
useEffect( ()=>{
},[term,results.length])
useEffect 包含多个参数时候,任一参数变化会触发方法被执行。
const [term,setTerm] = useState('programing')
const [debouncedTerm,setDebouncedTerm] = useState(term)
const [results, setResults] = useState([])
useEffect(()=>{
const timeoutId = setTimeout(()=>{
setDebouncedTerm(term)
},1000)
return () =>{
clearTimeout(timeoutId);
}
},[term])
useEffect(()=>{
const search = async ()=>{
await axios.get('https://en.wikipedia.org/w/api.php',{
params:{
action:'query',
list:'search',
origin:'*',
format:'json',
search:term,
}
}).then(response =>{
setResults(response.query.search)
})
.catch(error =>{
console.log(error.message)
})
}
search()
},[debouncedTerm])
如何控制因state频繁变更而引起的重复渲染,通过debounced(防抖)变量,使用两个不同的useEffect来实现
函数防抖一般用在什么情况之下呢?一般用在,连续的事件只需触发一次回调的场合。
在JSX代码中根据变量来显示不同内容的方法:
className={`ui selection dropdown ${dropState=='down'? selectionClass:''}`}
在上面的例子中,dropdown控件只能执行创建元素内的动作监听,无法完成此控件之外的事件监听。点击事件首先会在点击元素触发onclick 事件,执行完成后会向上级父元素移动触发各级的事件。
实际代码触发的顺序:
1. document.body.addEventListener('click',()=>{
2. 元素 onlick
3. 元素父级 onclick
为了避免组件内和组件外不同点击事件互相干扰,可以采取判断点击事件促发元素的方式,通过useRef指向组件顶级元素,来判断点击事件是否发生在组件内部,以此为条件执行代码:
具体现实:
import React, { useState , useEffect ,useRef} from "react";
const Dropdown = ({options,selected, onSelectedChange}) =>{
const [dropState,setDropState] = useState(false)
const ref = useRef()
// //only effect in intial process
useEffect(()=>{
document.body.addEventListener('click',(event)=>{
if(ref.current.contains(event.target)){
return
}
setDropState(false)
})
},[])
return (<div className="ui form" ref={ref}>
将ref指向顶级元素,并通过contains方法判断点击事件的控件是否在ref元素之内,如果是则不做任何改动,如果不是,说明点击了外部元素,就执行关闭dropdown状态的代码。
useEffect中的return函数的使用示例:
useEffect(()=>{
const onBodyClick = (event) =>{
if(ref.current.contains(event.target)){
return
}
setDropState(false)
}
document.body.addEventListener('click',onBodyClick)
return ()=>{
document.body.removeEventListener('click',onBodyClick)
}
},[])
第一次执行,向body添加click事件绑定函数,再通过return函数返回remove事件绑定的,这样下次调用此函数时候会首先remove事件,不会引起因组件被移除,事件绑定还在继续造成的空指针错误