基于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("請輸入正確的十六進制顏色值");
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">十六進制格式顏色:</label>
<input name="hexColor" ref={hexColor} type="text" placeholder="請輸入十六進制格式顏色"></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)于十六進制顏色值與RGB顏色值互轉(zhuǎn)的簡易工具,代碼里邊我們來看一看 useRef 鉤子的用法,我們用 useRef 鉤子創(chuàng)建了兩個 ref,分別在 JSX 中 input 標簽中添加 ref 屬性,那么我們就不再需要通過選擇器來獲取 DOM 元素了,直接在 ref 對象中去取 DOM 元素就可以了,從代碼中我們可以看到,這個 DOM 元素存儲在 ref 對象中的 current 屬性中。
受控組件
在 React 中,受控組件就是以組件內(nèi)部狀態(tài)來管理組件內(nèi)部值得變化的組件。而上一節(jié)我們提到了組件的狀態(tài)有 React 提供的 useState 鉤子來進行控制,我們可以理解成受控組件就是由狀態(tài)來控制屬性值變化得組件,由父組件屬性傳遞和事件來驅(qū)動狀態(tài)的變化,使數(shù)據(jù)產(chǎn)生流動的效果。當然,數(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("請輸入正確的十六進制顏色值");
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">十六進制格式顏色:</label>
<input name="hexColor" type="text" value={hexColorStr} placeholder="請輸入十六進制格式顏色" 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 重新渲染,將值又賦值進輸入框,形成一個閉合回路。
如果感覺看代碼太麻煩,難以理解,我們來看看示意圖:

我們把代碼拿出來理一理就能得到一個很簡單的邏輯圖,是不是一目了然。
上面我們利用狀態(tài)創(chuàng)建的受控組件中,input 元素只有兩個,但是在實際的開發(fā)中,對應(yīng)的 input 元素可能有十多二十個甚至更多,按照這樣的方法,那是不是得這樣重復操作很多次,之前我們提到過,遇到重復的,結(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("請輸入正確的十六進制顏色值");
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">十六進制格式顏色:</label>
<input {...hexColorProps} name="hexColor" type="text" placeholder="請輸入十六進制格式顏色"></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),當我們需要封裝自定義鉤子的時候,當函數(shù)能返回跟原始鉤子保持一致的結(jié)構(gòu)時,當在使用的時候,是不是就會更加的輕松呢?值得注意的是,這里的 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)換器的詳細內(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這個工具,覺得還不錯,功能很強大,所以記錄一下改工具的基礎(chǔ)用法,對React+TypeScript項目中使用CodeMirror的步驟感興趣的朋友跟隨小編一起看看吧2023-07-07
使用react在修改state中的數(shù)組和對象數(shù)據(jù)的時候(setState)
這篇文章主要介紹了使用react在修改state中的數(shù)組和對象數(shù)據(jù)的時候(setState),具有很好的參考價值,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-09-09
react-router-dom6(對比?router5)快速入門指南
這篇文章主要介紹了快速上手react-router-dom6(對比?router5),通過本文學習最新的react-router-dom?v6版本的路由知識,并且會與v5老版本進行一些對比,需要的朋友可以參考下2022-08-08

