基于React的狀態(tài)管理實現(xiàn)一個簡單的顏色轉(zhuǎn)換器
初探 React 鉤子
React 訪問 DOM 節(jié)點
在 React 中,要想直接訪問 DOM 節(jié)點需要使用 React 的一項特性—— ref。
ref:是一個對象,存儲一個組件整個生命周期內(nèi)的值。
在 React 中,React 為我們提供了 useRef 鉤子 來創(chuàng)建 ref。
import { useRef } from "react"; export default function ChangeColorForm() { const hexColor = useRef(); const rgbColor = useRef(); const getFullHexColorStr = str => { if (isNaN(parseInt(str, 16))) { alert("請輸入正確的十六進(jìn)制顏色值"); return ''; } if (str.length === 3) { return str.split('').map(item => item + item).join(''); } else { const lastChar = str.charAt(str.length - 1); const strArr = [...Array(5)]; return str + strArr.map(s => lastChar).join(""); } } const toggleColor = e => { e.preventDefault(); const hexColorStr = hexColor.current.value; const rgbColorStr = rgbColor.current.value; if (hexColorStr) { const hexColor = getFullHexColorStr(hexColorStr).toUpperCase(); if (!hexColor) { return; } const strArr = [...Array(6)]; const rgbColorArr = strArr.reduce((pre, cur, index) => { if (index % 2 === 1) { pre = [...pre, parseInt(hexColor.substring(index - 1, index + 1), 16)]; } return pre; },[]); rgbColor.current.value = rgbColorArr.join(','); } else { if (rgbColorStr) { const rgbColorArr = rgbColorStr.split(','); const isRightLength = rgbColorArr.length === 3; const isRightRGB = rgbColorArr.every(colorNum => !isNaN(Number(colorNum) && Number(colorNum) >= 0 && Number(colorNum) <= 255)) if (isRightLength && isRightRGB) { const hexColorArr = rgbColorArr.map(colorNum => Number(colorNum).toString(16)); hexColor.current.value = hexColorArr.join('').toUpperCase(); } else { alert("請輸入正確的rgb格式顏色值"); } } } }; return( <form onSubmit={toggleColor} style={{padding: '20px'}}> <label htmlFor="hexColor">十六進(jìn)制格式顏色:</label> <input name="hexColor" ref={hexColor} type="text" placeholder="請輸入十六進(jìn)制格式顏色"></input> <br/> <br/> <label htmlFor="rgbColor">rgb格式顏色:</label> <input name="rgbColor" ref={rgbColor} type="text" placeholder="請輸入rgb格式顏色"></input> <button>轉(zhuǎn)換</button> </form> ) }
這里我們手動寫了一個關(guān)于十六進(jìn)制顏色值與RGB顏色值互轉(zhuǎn)的簡易工具,代碼里邊我們來看一看 useRef 鉤子的用法,我們用 useRef 鉤子創(chuàng)建了兩個 ref,分別在 JSX 中 input 標(biāo)簽中添加 ref 屬性,那么我們就不再需要通過選擇器來獲取 DOM 元素了,直接在 ref 對象中去取 DOM 元素就可以了,從代碼中我們可以看到,這個 DOM 元素存儲在 ref 對象中的 current 屬性中。
受控組件
在 React 中,受控組件就是以組件內(nèi)部狀態(tài)來管理組件內(nèi)部值得變化的組件。而上一節(jié)我們提到了組件的狀態(tài)有 React 提供的 useState 鉤子來進(jìn)行控制,我們可以理解成受控組件就是由狀態(tài)來控制屬性值變化得組件,由父組件屬性傳遞和事件來驅(qū)動狀態(tài)的變化,使數(shù)據(jù)產(chǎn)生流動的效果。當(dāng)然,數(shù)據(jù)流動的效果也注定使組件不斷的重新渲染,這一點,我們也必須要了解。
既然這樣,我們來讓這個顏色碼值轉(zhuǎn)換表單組件改成一個受控組件:
import { useState } from "react"; export default function ChangeColorForm() { const [hexColorStr, setHexColorStr] = useState('FFFFFF'); const [rgbColorStr, setRgbColorStr] = useState(''); const getFullHexColorStr = str => { if (isNaN(parseInt(str, 16))) { alert("請輸入正確的十六進(jìn)制顏色值"); return ''; } if (str.length === 3) { return str.split('').map(item => item + item).join(''); } else { const lastChar = str.charAt(str.length - 1); const strArr = [...Array(5)]; return str + strArr.map(() => lastChar).join(""); } } const toggleColor = e => { e.preventDefault(); if (hexColorStr) { const hexColor = getFullHexColorStr(hexColorStr).toUpperCase(); if (!hexColor) { return; } const strArr = [...Array(6)]; const rgbColorArr = strArr.reduce((pre, cur, index) => { if (index % 2 === 1) { pre = [...pre, parseInt(hexColor.substring(index - 1, index + 1), 16)]; } return pre; },[]); setRgbColorStr(rgbColorArr.join(',')); } else { if (rgbColorStr) { const rgbColorArr = rgbColorStr.split(','); const isRightLength = rgbColorArr.length === 3; const isRightRGB = rgbColorArr.every(colorNum => !isNaN(Number(colorNum) && Number(colorNum) >= 0 && Number(colorNum) <= 255)) if (isRightLength && isRightRGB) { const hexColorArr = rgbColorArr.map(colorNum => Number(colorNum).toString(16)); setHexColorStr(hexColorArr.join('').toUpperCase()); } else { alert("請輸入正確的rgb格式顏色值"); } } } }; return( <form onSubmit={toggleColor} style={{padding: '20px'}}> <label htmlFor="hexColor">十六進(jìn)制格式顏色:</label> <input name="hexColor" type="text" value={hexColorStr} placeholder="請輸入十六進(jìn)制格式顏色" onChange={e => {setHexColorStr(e.target.value)}}></input> <br/> <br/> <label htmlFor="rgbColor">rgb格式顏色:</label> <input name="rgbColor" type="text" value={rgbColorStr} placeholder="請輸入rgb格式顏色" onChange={e => {setRgbColorStr(e.target.value)}}></input> <button>轉(zhuǎn)換</button> </form> ) }
在組件中,通過 React 的狀態(tài)來保存兩個 input 元素的值,只要觸發(fā)了 onChange 事件,通過參數(shù) e(event).target 來獲取 DOM,獲取元素值再通過狀態(tài)設(shè)置函數(shù)來將狀態(tài)值更新,使得 DOM 重新渲染,將值又賦值進(jìn)輸入框,形成一個閉合回路。
如果感覺看代碼太麻煩,難以理解,我們來看看示意圖:
我們把代碼拿出來理一理就能得到一個很簡單的邏輯圖,是不是一目了然。
上面我們利用狀態(tài)創(chuàng)建的受控組件中,input 元素只有兩個,但是在實際的開發(fā)中,對應(yīng)的 input 元素可能有十多二十個甚至更多,按照這樣的方法,那是不是得這樣重復(fù)操作很多次,之前我們提到過,遇到重復(fù)的,結(jié)構(gòu)相似的代碼,考慮一下是否可以抽象出來封裝一下呢?下面我們來介紹一下自定義的鉤子。
自定義鉤子
從上面的代碼我們可以看到,定義狀態(tài),input 元素中賦值 value,onChange 方法,他們的結(jié)構(gòu)是不是基本都是一樣的呢?我們先來看看定義狀態(tài)是不是可以抽象為:
// initValue 初始值參數(shù) const [value, setValue] = useState(initValue);
對于 input 元素中的屬性賦值,我們還能想起屬性較多的時候,整體屬性的傳遞方法嗎?估計能想起來{...props}
;那我們能否把 value,跟 onChange 封裝在一個對象里邊呢?然后用一個函數(shù)返回回來。我們新建一個 hooks.js文件,來看看怎么封裝。
// hooks.js import { useState } from "react"; export const useInput = initValue => { const [value, setValue] = useState(initValue); return [ {value, onChange: (e) => setValue(e.target.value)}, (custVal = initValue) => setValue(custVal) ] }
一眼看完,可能還沒明白為啥要這樣封裝,先暫時不說,我們還是再來把顏色轉(zhuǎn)換器繼續(xù)優(yōu)化一下,再來看看為啥這么封裝?
import { useInput } from "./hooks"; export default function ChangeColorForm() { const [hexColorProps, setHexColorStr] = useInput('FFFFFF'); const [rgbColorProps, setRgbColorStr] = useInput(''); const getFullHexColorStr = str => { if (isNaN(parseInt(str, 16))) { alert("請輸入正確的十六進(jìn)制顏色值"); return ''; } if (str.length === 3) { return str.split('').map(item => item + item).join(''); } else { const lastChar = str.charAt(str.length - 1); const strArr = [...Array(5)]; return str + strArr.map(() => lastChar).join(""); } } const toggleColor = e => { e.preventDefault(); if (hexColorProps.value) { const hexColor = getFullHexColorStr(hexColorProps.value).toUpperCase(); if (!hexColor) { return; } const strArr = [...Array(6)]; const rgbColorArr = strArr.reduce((pre, cur, index) => { if (index % 2 === 1) { pre = [...pre, parseInt(hexColor.substring(index - 1, index + 1), 16)]; } return pre; },[]); setRgbColorStr(rgbColorArr.join(',')); } else { if (rgbColorProps.value) { const rgbColorArr = rgbColorProps.value.split(','); const isRightLength = rgbColorArr.length === 3; const isRightRGB = rgbColorArr.every(colorNum => !isNaN(Number(colorNum) && Number(colorNum) >= 0 && Number(colorNum) <= 255)) if (isRightLength && isRightRGB) { const hexColorArr = rgbColorArr.map(colorNum => Number(colorNum).toString(16)); setHexColorStr(hexColorArr.join('').toUpperCase()); } else { alert("請輸入正確的rgb格式顏色值"); } } } }; const resetClick = e => { e.preventDefault(); setHexColorStr(); } return( <form onSubmit={toggleColor} style={{padding: '20px'}}> <label htmlFor="hexColor">十六進(jìn)制格式顏色:</label> <input {...hexColorProps} name="hexColor" type="text" placeholder="請輸入十六進(jìn)制格式顏色"></input> <button onClick={resetClick}>重置</button> <br/> <br/> <label htmlFor="rgbColor">rgb格式顏色:</label> <input {...rgbColorProps} name="rgbColor" type="text" placeholder="請輸入rgb格式顏色"></input> <button>轉(zhuǎn)換</button> </form> ) }
我們先來看看這兩行代碼的對比:
// useState React 鉤子 const [hexColorStr, setHexColorStr] = useState('FFFFFF'); // useInput 自定義鉤子 const [hexColorProps, setHexColorStr] = useInput('FFFFFF');
useState 鉤子我們已經(jīng)用得比較熟悉,熟悉了它的用法與結(jié)構(gòu),當(dāng)我們需要封裝自定義鉤子的時候,當(dāng)函數(shù)能返回跟原始鉤子保持一致的結(jié)構(gòu)時,當(dāng)在使用的時候,是不是就會更加的輕松呢?值得注意的是,這里的 hexColorProps 是包含了 value, onChange 的多屬性的屬性值,在使用的時候我們是需要注意跟 useState 定義的屬性值得區(qū)別。
在這次的代碼中,我故意添加了一個重置按鈕,估計用意你們已經(jīng)猜到,很直白,就是為了講解一下 hooks.js 中的這個方法:
(custVal = initValue) => setValue(custVal)
隨著 ESNext 的不斷更新,前端的代碼也是越來越簡單,但是只要我們慢慢分析,還是很好理解的,我們來看一下對應(yīng) ES5 的代碼:
function (custVal) { custVal = custVal ? custVal : initValue; return setValue(custVal) }
在實際的開發(fā)中,會遇到比較多的表單的聯(lián)動與重置,不難看出這個方法,就是為給 input 元素提供外部事件(非input 元素的 onChange 事件)來更改或重置 input元素的 value 屬性值。
總結(jié)
ref:是一個對象,存儲一個組件整個生命周期內(nèi)的值,DOM 元素存儲在 ref 對象中的 current 屬性中;
受控組件:是由狀態(tài)來控制屬性值變化得組件,由父組件屬性傳遞和事件來驅(qū)動狀態(tài)的變化,使數(shù)據(jù)產(chǎn)生流動的效果;
自定義鉤子:根據(jù)已知組件,方法的封裝,在封裝時盡可能保持與原組件,方法的參數(shù),返回值結(jié)構(gòu)保持一致。
以上就是基于React的狀態(tài)管理實現(xiàn)一個簡單的顏色轉(zhuǎn)換器的詳細(xì)內(nèi)容,更多關(guān)于React實現(xiàn)顏色轉(zhuǎn)換器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
React+TypeScript項目中使用CodeMirror的步驟
CodeMirror被廣泛應(yīng)用于許多Web應(yīng)用程序和開發(fā)工具,之前做需求用到過codeMirror這個工具,覺得還不錯,功能很強(qiáng)大,所以記錄一下改工具的基礎(chǔ)用法,對React+TypeScript項目中使用CodeMirror的步驟感興趣的朋友跟隨小編一起看看吧2023-07-07hooks寫React組件的5個注意細(xì)節(jié)詳解
這篇文章主要為大家介紹了hooks寫React組件的5個需要注意的細(xì)節(jié)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-03-03使用react在修改state中的數(shù)組和對象數(shù)據(jù)的時候(setState)
這篇文章主要介紹了使用react在修改state中的數(shù)組和對象數(shù)據(jù)的時候(setState),具有很好的參考價值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09解決react中l(wèi)abel標(biāo)簽for報錯問題
這篇文章主要介紹了react中l(wèi)abel標(biāo)簽for報錯問題,解決辦法就是react中l(wèi)abel標(biāo)簽沒有for屬性,用htmlFor代替for屬性,感興趣的朋友跟隨小編一起看看吧2022-02-02react-router-dom6(對比?router5)快速入門指南
這篇文章主要介紹了快速上手react-router-dom6(對比?router5),通過本文學(xué)習(xí)最新的react-router-dom?v6版本的路由知識,并且會與v5老版本進(jìn)行一些對比,需要的朋友可以參考下2022-08-08