欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

React Hook的使用示例

 更新時(shí)間:2021年04月06日 10:33:53   作者:鵬廠搬磚工  
這篇文章主要介紹了React Hook的使用示例,幫助大家更好的理解和學(xué)習(xí)使用React,感興趣的朋友可以了解下

這篇文章分享兩個(gè)使用React Hook以及函數(shù)式組件開發(fā)的簡單示例。

一個(gè)簡單的組件案例

Button組件應(yīng)該算是最簡單的常用基礎(chǔ)組件了吧。我們開發(fā)組件的時(shí)候期望它的基礎(chǔ)樣式能有一定程度的變化,這樣就可以適用于不同場景了。第二點(diǎn)是我在之前做項(xiàng)目的時(shí)候?qū)懸粋€(gè)函數(shù)組件,但這個(gè)函數(shù)組件會(huì)寫的很死板,也就是上面沒有辦法再綁定基本方法。即我只能寫入我已有的方法,或者特性。希望編寫B(tài)utton組件,即使沒有寫onClick方法,我也希望能夠使用那些自帶的默認(rèn)基本方法。

對(duì)于第一點(diǎn),我們針對(duì)不同的className,來寫不同的css,是比較好實(shí)現(xiàn)的。

第二點(diǎn)實(shí)現(xiàn)起略微困難。我們不能把Button的默認(rèn)屬性全部寫一遍,如果能夠把默認(rèn)屬性全部導(dǎo)入就好了。

事實(shí)上,React已經(jīng)幫我們實(shí)現(xiàn)了這一點(diǎn)。React.ButtonHTMLAttributes<HTMLElement>里面就包含了默認(rèn)的Button屬性??墒俏覀冇植荒苤苯邮褂眠@個(gè)接口,因?yàn)槲覀兊腂utton組件可能還有一些自定義的東西。對(duì)此,我們可以使用Typescript的交叉類型

type NativeButtonProps = MyButtonProps & React.ButtonHTMLAttributes<HTMLElement>

此外,我們還需要使用resProps來導(dǎo)入其他非自定義的函數(shù)或?qū)傩浴?/p>

下面是Button組件具體實(shí)現(xiàn)方案:

import React from 'react'
import classNames from 'classnames'

type ButtonSize = 'large' | 'small'
type ButtonType = 'primary' | 'default' | 'danger'

interface BaseButtonProps {
 className?: string;
 disabled?: boolean;
 size?: ButtonSize;
 btnType?: ButtonType;
 children?: React.ReactNode;
}

type NativeButtonProps = BaseButtonProps & React.ButtonHTMLAttributes<HTMLElement>
const Button: React.FC<NativeButtonProps>= (props) => {
 const {
 btnType,
 className,
 disabled,
 size,
 children,
 // resProps用于取出所有剩余屬性
 ...resProps
 } = props
 // btn, btn-lg, btn-primary
 const classes = classNames('btn', className, {
 [`btn-${btnType}`]: btnType,
 [`btn-${size}`]: size,
 'disabled': disabled
 })
 return (
 <button
  className={classes}
  disabled={disabled}
  {...resProps}
 >
  {children}
 </button>
 )
}

Button.defaultProps = {
 disabled: false,
 btnType: 'default'
}

export default Button

通過上面的方式,我們就可以在我們自定義的Button組件中使用比如onClick方法了。使用Button組件案例如下:

<Button disabled>Hello</Button>
<Button btnType='primary' size='large' className="haha">Hello</Button>
<Button btnType='danger' size='small' onClick={() => alert('haha')}>Test</Button>

展示效果如下:

在這個(gè)代碼中我們引入了一個(gè)新的npm package稱之為classnames,具體使用方式可以參考GitHub Classnames,使用它就可以很方便實(shí)現(xiàn)className的擴(kuò)展,它的一個(gè)簡單使用示例如下:

classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'

// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'

// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'

通過使用classNames,就可以很方便的在Button中添加個(gè)性化的屬性??梢钥吹綄?duì)于組件的HTML輸出結(jié)果中有hahaclassName:

<button class="btn haha btn-primary btn-lg">Hello</button>

與此同時(shí),我們上述代碼方式也解決了自定義組件沒有辦法使用默認(rèn)屬性和方法問題。

更復(fù)雜的父子組件案例

接下來我們展示一下如何用函數(shù)組件完成一個(gè)菜單功能。這個(gè)菜單添加水平模式和垂直模式兩種功能模式。點(diǎn)開某個(gè)菜單詳情,將這個(gè)詳情作為子組件。

當(dāng)然,菜單這個(gè)功能根本就不需要父組件傳數(shù)據(jù)到子組件(子組件指的是菜單詳情),我們?yōu)榱藢W(xué)習(xí)和演示如何將父組件數(shù)據(jù)傳給子組件,強(qiáng)行給他添加這個(gè)功能。有點(diǎn)畫蛇添足,大家理解一下就好。

首先介紹父子組件的功能描述。Menu是整體父組件,MenuItem是每一個(gè)具體的小菜單,SubMenu里面是可以點(diǎn)開的下拉菜單。

下圖是展開后的樣子:

整體代碼結(jié)構(gòu)如下:

<Menu defaultIndex={'0'} onSelect={(index) => {alert(index)}} mode="vertical" defaultOpenSubMenus={['2']}>
 <MenuItem index={'0'}>
 cool link
 </MenuItem>
 <MenuItem index={'1'}>
 cool link 2
 </MenuItem>
 <SubMenu title="dropdown">
 <MenuItem index={'3'}>
  dropdown 1
 </MenuItem>
 <MenuItem index={'4'}>
  dropdown 2
 </MenuItem>
 </SubMenu>
 <MenuItem index={'2'}>
 cool link 3
 </MenuItem>
</Menu>

在這個(gè)組件中,我們用到了useState,另外因?yàn)樯婕案附M件傳數(shù)據(jù)到子組件,所以還用到了useContext(父組件數(shù)據(jù)傳遞到子組件是指的父組件的index數(shù)據(jù)傳遞到子組件)。另外,我們還會(huì)演示使用自定義的onSelect來實(shí)現(xiàn)onClick功能(萬一你引入React泛型不成功,或者不知道該引入哪個(gè)React泛型,還可以用自定義的補(bǔ)救一下)。

如何寫onSelect

為了防止后面在代碼的汪洋大海中難以找到onSelect,這里先簡單的抽出來做一個(gè)onSelect書寫示例。比如我們在Menu組件中使用onSelect,它的使用方式和onClick看起來是一樣的:

<Menu onSelect={(index) => {alert(index)}}>

在具體這個(gè)Menu組件中具體使用onSelect可以這樣寫:

type SelectCallback = (selectedIndex: string) => void

interface MenuProps {
 onSelect?: SelectCallback;
}

實(shí)現(xiàn)handleClick的方法可以寫成這樣:

 const handleClick = (index: string) => {
 // onSelect是一個(gè)聯(lián)合類型,可能存在,也可能不存在,對(duì)此需要做判斷
 if (onSelect) {
  onSelect(index)
 }
 }

到時(shí)候要想把這個(gè)onSelect傳遞給子組件時(shí),使用onSelect: handleClick綁定一下就好。(可能你沒看太懂,我也不知道該咋寫,后面會(huì)有整體代碼分析,可能聯(lián)合起來看會(huì)比較容易理解)

React.Children

在講解具體代碼之前,還要再說說幾個(gè)小知識(shí)點(diǎn),其中一個(gè)是React.Children。

React.Children 提供了用于處理 this.props.children 不透明數(shù)據(jù)結(jié)構(gòu)的實(shí)用方法。

為什么我們會(huì)需要使用React.Children呢?是因?yàn)槿绻婕暗礁附M件數(shù)據(jù)傳遞到子組件時(shí),可能需要對(duì)子組件進(jìn)行二次遍歷或者進(jìn)一步處理。但是我們不能保證子組件是到底有沒有,是一個(gè)還是兩個(gè)或者多個(gè)。

this.props.children 的值有三種可能:如果當(dāng)前組件沒有子節(jié)點(diǎn),它就是 undefined ;如果有一個(gè)子節(jié)點(diǎn),數(shù)據(jù)類型是 object ;如果有多個(gè)子節(jié)點(diǎn),數(shù)據(jù)類型就是 array 。所以,處理 this.props.children 的時(shí)候要小心[1]。

React 提供一個(gè)工具方法 React.Children 來處理 this.props.children 。我們可以用 React.Children.map 來遍歷子節(jié)點(diǎn),而不用擔(dān)心 this.props.children 的數(shù)據(jù)類型是 undefined 還是 object[1]。

所以,如果有父子組件的話,如果需要進(jìn)一步處理子組件的時(shí)候,我們可以使用React.Children來遍歷,這樣不會(huì)因?yàn)閠his.props.children類型變化而出錯(cuò)。

React.cloneElement

React.Children出現(xiàn)時(shí)往往可能伴隨著React.cloneElement一起出現(xiàn)。因此,我們也需要介紹一下React.cloneElement。

在開發(fā)復(fù)雜組件中,經(jīng)常會(huì)根據(jù)需要給子組件添加不同的功能或者顯示效果,react 元素本身是不可變的 (immutable) 對(duì)象, props.children 事實(shí)上并不是 children 本身,它只是 children 的描述符 (descriptor) ,我們不能修改任何它的任何屬性,只能讀到其中的內(nèi)容,因此 React.cloneElement 允許我們拷貝它的元素,并且修改或者添加新的 props 從而達(dá)到我們的目的[2]。

例如,有的時(shí)候我們需要對(duì)子元素做進(jìn)一步處理,但因?yàn)镽eact元素本身是不可變的,所以,我們需要對(duì)其克隆一份再做進(jìn)一步處理。在這個(gè)Menu組件中,我們希望它的子組件只能是MenuItem或者是SubMenu兩種類型,如果是其他類型就會(huì)報(bào)警告信息。具體來說,可以大致將代碼寫成這樣:

if (displayName === 'MenuItem' || displayName === 'SubMenu') {
 // 以element元素為樣本克隆并返回新的React元素,第一個(gè)參數(shù)是克隆樣本
 return React.cloneElement(childElement, {
 index: index.toString()
 })
} else {
 console.error("Warning: Menu has a child which is not a MenuItem component")
}

父組件數(shù)據(jù)如何傳遞給子組件

通過使用Context來實(shí)現(xiàn)父組件數(shù)據(jù)傳遞給子組件。如果對(duì)Context不太熟悉的話,可以參考官方文檔,Context,在父組件中我們通過createContext來創(chuàng)建Context,在子組件中通過useContext來獲取Context。

index數(shù)據(jù)傳遞

Menu組件中實(shí)現(xiàn)父子組件中數(shù)據(jù)傳遞變量主要是index。

最后附上完整代碼,首先是Menu父組件:

import React, { useState, createContext } from 'react'
import classNames from 'classnames'
import { MenuItemProps } from './menuItem'

type MenuMode = 'horizontal' | 'vertical'
type SelectCallback = (selectedIndex: string) => void

export interface MenuProps {
 defaultIndex?: string; // 用于哪個(gè)menu子組件是高亮顯示
 className?: string;
 mode?: MenuMode;
 style?: React.CSSProperties;
 onSelect?: SelectCallback; // 點(diǎn)擊子菜單時(shí)可以觸發(fā)回調(diào) 
 defaultOpenSubMenus?: string[]; 
}

// 確定父組件傳給子組件的數(shù)據(jù)類型
interface IMenuContext {
 index: string;
 onSelect?: SelectCallback;
 mode?: MenuMode;
 defaultOpenSubMenus?: string[]; // 需要將數(shù)據(jù)傳給context
}

// 創(chuàng)建傳遞給子組件的context
// 泛型約束,因?yàn)閕ndex是要輸入的值,所以這里寫一個(gè)默認(rèn)初始值
export const MenuContext = createContext<IMenuContext>({index: '0'})

const Menu: React.FC<MenuProps> = (props) => {
 const { className, mode, style, children, defaultIndex, onSelect, defaultOpenSubMenus} = props
 // MenuItem處于active的狀態(tài)應(yīng)該是有且只有一個(gè)的,使用useState來控制其狀態(tài)
 const [ currentActive, setActive ] = useState(defaultIndex)
 const classes = classNames('menu-demo', className, {
 'menu-vertical': mode === 'vertical',
 'menu-horizontal': mode === 'horizontal'
 })

 // 定義handleClick具體實(shí)現(xiàn)點(diǎn)擊menuItem之后active變化
 const handleClick = (index: string) => {
 setActive(index)
 // onSelect是一個(gè)聯(lián)合類型,可能存在,也可能不存在,對(duì)此需要做判斷
 if (onSelect) {
  onSelect(index)
 }
 }

 // 點(diǎn)擊子組件的時(shí)候,觸發(fā)onSelect函數(shù),更改高亮顯示
 const passedContext: IMenuContext = {
 // currentActive是string | undefined類型,index是number類型,所以要做如下判斷進(jìn)一步明確類型
 index: currentActive ? currentActive : '0',
 onSelect: handleClick, // 回調(diào)函數(shù),點(diǎn)擊子組件時(shí)是否觸發(fā)
 mode: mode,
 defaultOpenSubMenus,
 }

 const renderChildren = () => {
 return React.Children.map(children, (child, index) => {
  // child里面包含一大堆的類型,要想獲得我們想要的類型來提供智能提示,需要使用類型斷言  
  const childElement = child as React.FunctionComponentElement<MenuItemProps>
  const { displayName } = childElement.type
  if (displayName === 'MenuItem' || displayName === 'SubMenu') {
  // 以element元素為樣本克隆并返回新的React元素,第一個(gè)參數(shù)是克隆樣本
  return React.cloneElement(childElement, {
   index: index.toString()
  })
  } else {
  console.error("Warning: Menu has a child which is not a MenuItem component")
  }
 })
 }
 return (
 <ul className={classes} style={style}>
  <MenuContext.Provider value={passedContext}>
  {renderChildren()}
  </MenuContext.Provider>
 </ul>
 )
}

Menu.defaultProps = {
 defaultIndex: '0',
 mode: 'horizontal',
 defaultOpenSubMenus: []
}

export default Menu

然后是MenuItem子組件:

import React from 'react'
import { useContext } from 'react'
import classNames from 'classnames'
import { MenuContext } from './menu'

export interface MenuItemProps {
 index: string;
 disabled?: boolean;
 className?: string;
 style?: React.CSSProperties;
}

const MenuItem: React.FC<MenuItemProps> = (props) => {
 const { index, disabled, className, style, children } = props
 const context = useContext(MenuContext)
 const classes = classNames('menu-item', className, {
 'is-disabled': disabled,
 // 實(shí)現(xiàn)高亮的具體邏輯
 'is-active': context.index === index
 })
 const handleClick = () => {
 // disabled之后就不能使用onSelect,index因?yàn)槭强蛇x的,所以可能不存在,需要用typeof來做一個(gè)判斷
 if (context.onSelect && !disabled && (typeof index === 'string')) {
  context.onSelect(index)
 }
 }
 return (
 <li className={classes} style={style} onClick={handleClick}>
  {children}
 </li>
 )
}

MenuItem.displayName = 'MenuItem'
export default MenuItem

最后是SubMenu子組件:

import React, { useContext, FunctionComponentElement, useState } from 'react'
import classNames from 'classnames'
import { MenuContext } from './menu'
import { MenuItemProps } from './menuItem'

export interface SubMenuProps {
 index?: string;
 title: string;
 className?: string
}

const SubMenu: React.FC<SubMenuProps> = ({ index, title, children, className }) => {
 const context = useContext(MenuContext)
 // 接下來會(huì)使用string數(shù)組的一些方法,所以先進(jìn)行類型斷言,將其斷言為string數(shù)組類型
 const openedSubMenus = context.defaultOpenSubMenus as Array<string>
 // 使用include判斷有沒有index
 const isOpened = (index && context.mode === 'vertical') ? openedSubMenus.includes(index) : false
 const [ menuOpen, setOpen ] = useState(isOpened) // isOpened返回的會(huì)是true或者false,這樣就是一個(gè)動(dòng)態(tài)值
 const classes = classNames('menu-item submenu-item', className, {
 'is-active': context.index === index
 })
 // 用于實(shí)現(xiàn)顯示或隱藏下拉菜單
 const handleClick = (e: React.MouseEvent) => {
 e.preventDefault()
 setOpen(!menuOpen)
 }
 let timer: any
 // toggle用于判斷是打開還是關(guān)閉
 const handleMouse = (e: React.MouseEvent, toggle: boolean) => {
 clearTimeout(timer)
 e.preventDefault()
 timer = setTimeout(()=> {
  setOpen(toggle)
 }, 300)
 }
 // 三元表達(dá)式,縱向
 const clickEvents = context.mode === 'vertical' ? {
 onClick: handleClick
 } : {}
 const hoverEvents = context.mode === 'horizontal' ? {
 onMouseEnter: (e: React.MouseEvent) => { handleMouse(e, true) },
 onMouseLeave: (e: React.MouseEvent) => { handleMouse(e, false) },
 } : {}

 // 用于渲染下拉菜單中的內(nèi)容
 // 返回兩個(gè)值,第一個(gè)是child,第二個(gè)是index,用i表示
 const renderChildren = () => {
 const subMenuClasses = classNames('menu-submenu', {
  'menu-opened': menuOpen
 })
 // 下面功能用于實(shí)現(xiàn)在subMenu里只能有MenuItem
 const childrenComponent = React.Children.map(children, (child, i) => {
  const childElement = child as FunctionComponentElement<MenuItemProps>
  if (childElement.type.displayName === 'MenuItem') {
  return React.cloneElement(childElement, {
   index: `${index}-${i}`
  })
  } else {
  console.error("Warning: SubMenu has a child which is not a MenuItem component")
  }
 })
 return (
  <ul className={subMenuClasses}>
  {childrenComponent}
  </ul>
 )
 }
 return (
 // 展開運(yùn)算符,向里面添加功能,hover放在外面
 <li key={index} className={classes} {...hoverEvents}>
  <div className="submenu-title" {...clickEvents}>
  {title}
  </div>
  {renderChildren()}
 </li>
 )
}

SubMenu.displayName = 'SubMenu'
export default SubMenu

參考資料

以上就是React Hook的使用示例的詳細(xì)內(nèi)容,更多關(guān)于React Hook的使用的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解react-router 4.0 下服務(wù)器如何配合BrowserRouter

    詳解react-router 4.0 下服務(wù)器如何配合BrowserRouter

    這篇文章主要介紹了詳解react-router 4.0 下服務(wù)器如何配合BrowserRouter,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-12-12
  • 關(guān)于React動(dòng)態(tài)加載路由處理的相關(guān)問題

    關(guān)于React動(dòng)態(tài)加載路由處理的相關(guān)問題

    這篇文章主要介紹了關(guān)于React動(dòng)態(tài)加載路由處理的相關(guān)問題,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-01-01
  • antd?table動(dòng)態(tài)修改表格高度的實(shí)現(xiàn)

    antd?table動(dòng)態(tài)修改表格高度的實(shí)現(xiàn)

    本文主要介紹了antd?table動(dòng)態(tài)修改表格高度的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-07-07
  • React使用react-sortable-hoc如何實(shí)現(xiàn)拖拽效果

    React使用react-sortable-hoc如何實(shí)現(xiàn)拖拽效果

    這篇文章主要介紹了React使用react-sortable-hoc如何實(shí)現(xiàn)拖拽效果問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-07-07
  • React-vscode使用jsx語法的問題及解決方法

    React-vscode使用jsx語法的問題及解決方法

    很多朋友在安裝插件ES7 React/Redux/GraphQL/React-Native snippets還是不能完全支持jsx語法,糾結(jié)是什么原因呢,該如何處理呢,下面小編給大家分享本文幫助大家解決React-vscode使用jsx語法問題,感興趣的朋友一起看看吧
    2021-06-06
  • React18新增特性介紹

    React18新增特性介紹

    react歷次版本迭代主要想解決的是兩類導(dǎo)致網(wǎng)頁卡頓的問題,分別是cpu密集型任務(wù)和io密集型任務(wù)導(dǎo)致的卡頓問題,react18新增特性就是為了解決上述問題
    2022-09-09
  • react?hooks?計(jì)數(shù)器實(shí)現(xiàn)代碼

    react?hooks?計(jì)數(shù)器實(shí)現(xiàn)代碼

    這篇文章主要介紹了react?hooks計(jì)數(shù)器實(shí)現(xiàn)代碼,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08
  • react-router-dom入門使用教程(前端路由原理)

    react-router-dom入門使用教程(前端路由原理)

    這篇文章主要介紹了react-router-dom入門使用教程,主要包括react路由相關(guān)理解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-08-08
  • React中的useEffect useLayoutEffect到底怎么用

    React中的useEffect useLayoutEffect到底怎么用

    這篇文章主要介紹了React中的useEffect useLayoutEffect具體使用方法,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2023-02-02
  • react-native動(dòng)態(tài)切換tab組件的方法

    react-native動(dòng)態(tài)切換tab組件的方法

    在APP中免不了要使用tab組件,有的是tab切換,也有的是tab分類切換.這篇文章主要介紹了react-native動(dòng)態(tài)切換tab組件的方法,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2018-07-07

最新評(píng)論