泰州模板建站源码,购书网站开发,iis新建网站无法浏览,昌吉做网站React高级 高阶组件 HOC属性代理反向继承属性代理和反向继承的区别实例实例一实例二 HooksHooks APIuseState#xff1a;useEffect#xff1a;useLayoutEffect#xff1a;useRef#xff1a;useContext#xff1a;useReducer:useMemouseCallback 自定义Hooks 拓展#xff… React高级 高阶组件 HOC属性代理反向继承属性代理和反向继承的区别实例实例一实例二 HooksHooks APIuseStateuseEffectuseLayoutEffectuseRefuseContextuseReducer:useMemouseCallback 自定义Hooks 拓展 ios、安卓、h5、小程序、app在移动端开发上有啥区别 应用场景不同 原生的native app开发native需要客户端的发版才能做更新迭代客户端嵌套页面webview 和 混合应用hybrid app开发 — 要做快的发版更新迭代的话mini program - 小程序开发 高阶组件 HOC
hoc:higher order component 本身不是复杂的内容也不是React官方提供他是一种设计模式组成的高阶用法接收一个组件作为参数并返回一个新的组件React是用组件拼装页面的 简单来说hoc是组件作为参数返回值也是组件 HOC 主要用于代码复用类似于Vue中的mixin
function HOC(WrappedComponent){return propsWrappedComponent {...props} /
}这里的props传递可以看作是 vue中的this.$slots.defaults传递一样
//类的使用
function HOC(WrappedComponent){return class extends React.Component{constructor(props){super(props)}render(){const newProps{name:zhangsan}return WrappedComponent {...props} {...newProps} /}}
}class类的完整示例
import React from react;// 创建 HOC增强组件功能withCounter 是我们定义的高阶组件它接收一个组件WrappedComponent并返回一个新的组件。
function withCounter(WrappedComponent) {return class extends React.Component {constructor(props) {super(props);this.state { count: 0 };}increment () {this.setState(prevState ({ count: prevState.count 1 }));};render() {return (WrappedComponent{...this.props}count{this.state.count} // 将新的 statecount传递给原组件increment{this.increment} // 将新的方法increment传递给原组件/);}};
}// 创建一个基础组件接收 count 和 increment 作为 props
function Counter({ count, increment }) {return (divpCount: {count}/pbutton onClick{increment}Increment/button/div);
}// 使用 HOC 包装 Counter 组件
const EnhancedCounter withCounter(Counter);// 使用增强后的组件
export default function App() {return EnhancedCounter /;
}
分类
属性代理反向继承
属性代理
返回的是原本的组件
代理props
function HOC(WrappedComponent){const newProps {name:zhangsan }return propsWrappedComponent {...props} {...newProps} /
}模拟state
function HOC(WrappedComponent){return class extends React.Component {constructor(props){super(props)this.state {name:zhangsan}this.onChange this.onChange.bind(this)}onChange(e){this.setState({name:e.target.value})}render(){const newProps{name: {value: this.state.name,onChange:this.onChange}}return WrappedComponent {...this.props} {...newProps} /}}
}function Demo(props) {const { name } propsconst { value, onChange } name;//props// 定义新的state方法
}// 在外部的组件中定义的state state:name,methods:onChange
// 传递给内部的 WrappedComponent通过props的方式去传递给他能够获取value和onChange事件然后去消费它条件渲染基于返回组件做的自定义处理
function HOC(WrappedComponent) {// customif (show false) {return div暂无数据/div}return props WrappedComponent {...props} /
}反向继承
使用类组件的方式 返回的是新的组件
function HOC(WrappedComponent) {return class extends WrappedComponent{render() {return super.render();}}
}这里返回的新组件是原先的组件一比一复制过来的。
使用
function HOC(WrappedComponent) {// componentDidMount 新加功能const didMount WrappedComponent.prototype.componentDidMount;//原本组件的mount方法return class extends WrappedComponent{// constructor() { this.stateWrappedComponent.prototype.state}componentDidMount() {if (didMount) {//需要将这样的生命周期关联的事件绑定到自定义的类中didMount.bind(this) //新的组件里面将原本组件生命周期方法执行了一遍}// handle custom 自定义逻辑this.setState({number:2})}render() {return super.render();}}
}举例使用 计算组件渲染时间
function withTiming(WrappedComponent) {return class extends WrappedComponent{constructor(props) {super(props);start 0;end 0;}componentWillMount() { //继承链上的生命周期方法依次执行由子到父的顺序执行也就是说先执行 withTiming 中的 componentWillMount然后执行 WrappedComponent 中的 componentWillMountif (super.componentWillMount) {super.componentWillMount();}startDate.now()}componentDidMount() { //同理先执行 withTiming 中的 componentDidMount然后执行 WrappedComponent 中的 componentDidMountif (super.componentDidMount) {super.componentDidMount();}end Date.now()console.error(${WrappedComponent.name}渲染时间为${end - start}ms) //也就是说这里统计的渲染时间实际是继承链上所有组件生命周期渲染时间的总和}render() {return super.render();}}
}
export default withTiming(Home)属性代理和反向继承的区别
属性代理本质上是在外部操作这个组件由外到内但是反向继承是由内到外返回一个新的组件。所以说由内部操作的话是可以操作组件的所有逻辑的内部逻辑相对比较危险。
实例
实例一
// views/PageA.js
import React from react;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;class PageA extends React.Component {state {movieList: [],}/* ... */async componentDidMount() {const movieList await fetchMovieListByType(comedy);this.setState({movieList,});}render() {return MovieList data{this.state.movieList} emptyTips暂无喜剧/}
}
export default PageA;// views/PageB.js
import React from react;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;class PageB extends React.Component {state {movieList: [],}// ...async componentDidMount() {const movieList await fetchMovieListByType(action);this.setState({movieList,});}render() {return MovieList data{this.state.movieList} emptyTips暂无动作片/}
}
export default PageB;// 冗余代码过多
// HOC
import React from react;const withFetchingHOC (WrappedComponent, fetchingMethod, defaultProps) {return class extends React.Component {async componentDidMount() {const data await fetchingMethod();this.setState({data,});}render() {return (WrappedComponent data{this.state.data} {...defaultProps} {...this.props} /);}}
}// 使用
// views/PageA.js
import React from react;
import withFetchingHOC from ../hoc/withFetchingHOC;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;const defaultProps {emptyTips: 暂无喜剧}export default withFetchingHOC(MovieList, fetchMovieListByType(comedy), defaultProps);// views/PageB.js
import React from react;
import withFetchingHOC from ../hoc/withFetchingHOC;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;const defaultProps {emptyTips: 暂无动作片}export default withFetchingHOC(MovieList, fetchMovieListByType(action), defaultProps);;// views/PageOthers.js
import React from react;
import withFetchingHOC from ../hoc/withFetchingHOC;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;
const defaultProps {...}
export default withFetchingHOC(MovieList, fetchMovieListByType(some-other-type), defaultProps);一般在工作中使用到的都是属性代理很少用到反向继承在统计性能指标的场景中可以用反向继承。 属性代理效果一般做公用逻辑的抽离 属性代理实例 不用HOC: views/PageA.js
// views/PageA.js
import React from react;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;// PageA喜剧片的渲染
class PageA extends React.Component {state {movieList: [],}/* ... */async componentDidMount() {const movieList await fetchMovieListByType(comedy);this.setState({movieList,});}render() {return MovieList data{this.state.movieList} emptyTips暂无喜剧/}
}
export default PageA;views/PageB.js
// views/PageB.js
import React from react;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;// pageB动作片的渲染
class PageB extends React.Component {state {movieList: [],}// ...async componentDidMount() {const movieList await fetchMovieListByType(action);this.setState({movieList,});}render() {return MovieList data{this.state.movieList} emptyTips暂无动作片/}
}
export default PageB;冗余代码过多使用HOC抽离
// 冗余代码过多
// HOC
import React from react;// 包裹的组件 请求的方法 上述的emptyTips
const withFetchingHOC (WrappedComponent, fetchingMethod, defaultProps) {return class extends React.Component {async componentDidMount() {const data await fetchingMethod();this.setState({data,});}render() {return (WrappedComponent data{this.state.data} {...defaultProps} {...this.props} /);}}
}views/PageA.js
// 使用
// views/PageA.js
import React from react;
import withFetchingHOC from ../hoc/withFetchingHOC;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;const defaultProps {emptyTips: 暂无喜剧}export default withFetchingHOC(MovieList, fetchMovieListByType(comedy), defaultProps);views/PageB.js
// views/PageB.js
import React from react;
import withFetchingHOC from ../hoc/withFetchingHOC;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;const defaultProps {emptyTips: 暂无动作片}export default withFetchingHOC(MovieList, fetchMovieListByType(action), defaultProps);其他页面
// views/PageOthers.js
import React from react;
import withFetchingHOC from ../hoc/withFetchingHOC;
import fetchMovieListByType from ../lib/utils;
import MovieList from ../components/MovieList;
const defaultProps {...}
export default withFetchingHOC(MovieList, fetchMovieListByType(some-other-type), defaultProps);实例二
在vue中有自定义指令像v-permission通过自定义指令实现按钮的逻辑展示但是在这里也能通过HOC来实现。 例如
import React from react;
import { whiteListAuth } from ../lib/utils; // 鉴权方法function AuthWrapper(WrappedComponent) {return class AuthWrappedComponent extends React.Component {constructor(props) {super(props);this.state {permissionDenied: -1,};}async componentDidMount() {try {await whiteListAuth(); // 请求鉴权接口this.setState({permissionDenied: 0,});} catch (err) {this.setState({permissionDenied: 1,});}}render() {if (this.state.permissionDenied -1) {return null; // 鉴权接口请求未完成}if (this.state.permissionDenied) {return div功能即将上线敬请期待~/div;}return WrappedComponent {...this.props} /;}}
}export default AuthWrapper;Hooks
上节内容回顾
在props和state两个组件开发过程中如果要定义组件内部中的变量使用props还是state state在props过程中props元素随着外部组件变化而变化如何观察到props的一个变化呢 生命周期等只要能够获取到新的props就可以props是外部传参的外部传递参数可能是不同的想要一个组件要改变它的props用什么样的方式能够做到呢 dispatch最直接的方式将props转为state将React组件封装的更好根据这里的思路官方中文链接将共有的能力抽离出来放到对应的stateprops上即可。
类组件中不能使用hooks
Hooks API
Hooks官方
结合官网中看下面讲到的几个API就行
use试验期在React 19中才会用到返回的是一个promise的结果这个方法是用来读取promise的返回值的。能够获取到返回的结果能够读取到context不建议使用。
useState const [state, setState] useState(initialState) initialState初始默认值state默认值的返回setState就是修改state的方式 import { useState } from react;function MyComponent() {const [age, setAge] useState(28);const [name, setName] useState(Taylor);const [todos, setTodos] useState(() createTodos()); //初始值能够获取函数的返回结果但是必须是同步的const [userInfo,setUserInfo]useState({name:Taylor,age:42})// userInfo.age52 这种方法是不生效的//这样去修改对象种某个属性的值setUserInfo({...userInfo,age:52})...
}useEffect
添加随着状态的改变来触发它的动作辅佐用返回的结果用来清除它的辅佐用
import { useEffect } from react;
import { createConnection } from ./chat.js;function ChatRoom({ roomId }) {const [serverUrl, setServerUrl] useState(https://localhost:1234);useEffect(() { //useEffect参数1.接收一个函数 2.一个数组const connection createConnection(serverUrl, roomId); //创建长连接connection.connect();return () { //返回这里用来销毁连接 connection.disconnect();};}, [serverUrl, roomId]); //当且仅当serverUrl或roomId发生变化时会重新执行useEffect中的函数// ...
}使用场景 可以在请求数据中使用
import { useState, useEffect } from react;
import { fetchBio } from ./api.js;export default function Page() {const [person, setPerson] useState(Alice);const [bio, setBio] useState(null);useEffect(() {let ignore false;setBio(null);fetchBio(person).then(result {if (!ignore) {setBio(result);}});return () {ignore true;}}, [person]);return (select value{person} onChange{e {setPerson(e.target.value);}}option valueAliceAlice/optionoption valueBobBob/optionoption valueTaylorTaylor/option/selecthr /pi{bio ?? Loading...}/i/p/);
}
代码中的useEffect: useEffect(() {let ignore false;setBio(null);fetchBio(person).then(result {if (!ignore) {setBio(result);}});return () {ignore true;}}, [person]);如果第二个返回的数组中person没有值了那么就是没有依赖项发生变化就在初始化中执行一次等同于 componentDidMount
useLayoutEffect
是useEffect的一个版本这两个传递的参数都一样只不过触发setup函数的时间不一样 useEffect: 组件mount - dom渲染 - useEffect((){},[deps]) useLayoutEffect组件mount - useLayoutEffect((){},[deps]) - dom渲染
const DemoUseLayoutEffect () {const target useRef()useLayoutEffect(() {/*我们需要在dom绘制之前移动dom到制定位置*/const { x ,y } getPositon() /* 获取要移动的 x,y坐标 */animate(target.current,{ x,y })}, []);return (div span ref{ target } classNameanimate/span/div)
}这里意味着先执行的useLayoutEffect的回调函数(执行动作animate)再去渲染的return返回中的div代码
需要在dom上渲染最终的结果就使用useLayoutEffect 需要在dom上展示执行callback回调函数的动作就使用useEffect
useRef
与Vue3中的Ref类似通过 ref.current 获取值但是创建的 ref 并没有关联到元素上ref 绑定的是真实的js对象 与 state区别 const [a,setA]useState(1) 当执行setA时候会执行 重新渲染 re-render 但是如果只想改变值并不希望组件重新渲染可以借助ref const auseRef(1) a.current2 不会触发视图的响应也不会触发视图的更新
使用场景
const inputRef useRef(null);.......
return input ref{inputRef} /;function handleClick() {inputRef.current.focus();
}useContext
/* 用useContext方式 */
const DemoContext () {const value useContext(Context);/ my name is aaa /return div my name is { value.name }/div
}/ 用Context.Consumer 方式 /
const DemoContext1 (){return Context.Consumer{/ my name is aaa */}{ (value) div my name is { value.name }/div }/Context.Consumer
}export default (){return divContext.Provider value{{ name:aaa }} DemoContext /DemoContext1 //Context.Provider/div
}创建的Context是能通过Provider和Consumer两个地方去消费的 第一种是用useContext将我们的值关联起来 第二种通过Provider将value值关联起来了就能在provider下层的所有节点上这里是DemoContextDemoContext1 这两个节点一方面通过 useContext获取到其中关联的value另一种是通过Context.Consumer之间去获取。但是在平常开发过程中一般是通过useContext和Provider去获取上层传递下来的状态
useReducer:
const DemoUseReducer (){/* number为更新后的state值, dispatchNumbner 为当前的派发函数 */const [ number , dispatchNumbner ] useReducer((state, action) {const { payload , name } action/ return的值为新的state /switch(name) {case a:return state 1case b:return state - 1 case c:return payload }return state}, 0)return div当前值{ number }{ / 派发更新 / }button onClick{()dispatchNumbner({ name: a })} 增加/buttonbutton onClick{()dispatchNumbner({ name: b })} 减少/buttonbutton onClick{()dispatchNumbner({ name: c , payload:666 })} 赋值/button{ / 把dispatch 和 state 传递给子组件 */ }MyChildren dispatch{ dispatchNumbner } State{{ number }} //div
}上述这两个useContext 和 useReducer 和Redux的逻辑是一样的 useMemo
是平常做响应式依赖的一个动作类似于vue中的computed
// selectList 不更新时不会重新渲染减少不必要的循环渲染
const auseMemo(() (div{selectList.map((i, v) (spanclassName{style.listSpan}key{v} {i.patentName} /span))}/div
), [selectList])当selectList元素变化时候重新执行回调函数只不过useMemo返回的结果就是这个函数执行的结果所以在selectList不变的话哪怕这个组件变化这个a的结果也不会发生变化 适用于性能优化
// listshow, cacheSelectList 不更新时不会重新渲染子组件
useMemo(() (Modalwidth{70%}visible{listshow}footer{[Button keyback 取消/Button,Buttonkeysubmittypeprimary确定/Button]} { /* 减少了PatentTable组件的渲染 */ }PatentTablegetList{getList}selectList{selectList}cacheSelectList{cacheSelectList}setCacheSelectList{setCacheSelectList}//Modal), [listshow, cacheSelectList])// 减少组件更新导致函数重新声明const DemoUseMemo () {/ 用useMemo 包裹之后的log函数可以避免了每次组件更新再重新声明 可以限制上下文的执行 /const newLog useMemo(() {const log () {console.log(123)}return log}, [])return div onClick{() newLog() } /div
}// 如果没有加相关的更新条件是获取不到更新之后的state的值的
const DemoUseMemo () {const [ number ,setNumber ] useState(0)const newLog useMemo(() {const log () {/ 点击span之后 打印出来的number 不是实时更新的number值 /console.log(number)}return log/ [] 没有 number */ }, [])return divdiv onClick{() newLog()} 打印/divspan onClick{ () setNumber( number 1 ) } 增加/span/div
}useCallback
useMemo返回的是函数执行的结果useCallback返回的是这个函数
挂载时初始化constructor 对应着 useLayoutEffect 初始化 componentDidMount 对应着 useLayoutEffect第二个参数不传 更新 对应着 useEffect 传递参数 shouldComponentUpdate 对应着 useMemo和useCallback componentDidUpdate 对应着 useEffect 回调函数执行 componentWillUnmount 对应着 useEffect 返回函数的执行
也就是这样
useLayoutEffect(() {// componentDidMountreturn () {// componentWillUnmount};
}, [])useEffect(() {// componentDidUpdate
}, [deps])// shouldComponentUpdate
useMemo(); useCallback()自定义Hooks
类似 const [a,b,c,d]useXXX(params1,parmas2) 这种形式的就叫做自定义hook 自定义hook中使用reactive中已有的hook
本质上是用已有的hooks做相同某种功能的聚合聚合后能够在其他某个地方取消费
const useTitle (title) { useEffect(() { document.title title; }, []);
}const App () {useTitle(Hello World);return divHello World/div;
}保证代码是相对独立的
import { useState, useEffect } from reactconst useScroll (scrollRef) {const [pos, setPos] useState([0,0])useEffect(() {function handleScroll(e){setPos([scrollRef.current.scrollLeft, scrollRef.current.scrollTop])}scrollRef.current.addEventListener(scroll, handleScroll)return () {scrollRef.current.removeEventListener(scroll, handleScroll)}}, [])return pos
}export default useScroll// 用法
import React, { useRef } from react
import { useScroll } from hooksconst Home (props) {const scrollRef useRef(null)const [x, y] useScroll(scrollRef)return divdiv ref{scrollRef}div classNameinnerBox/div/divdiv{ x }, { y }/div/div
}