最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

react編寫可編輯標(biāo)題示例詳解

 更新時間:2022年08月23日 16:59:49   作者:捌玖ki  
這篇文章主要為大家介紹了react編寫可編輯標(biāo)題示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

需求

因為自己換工作到了新公司,上周入職,以前沒有使用過react框架,雖然前面有學(xué)習(xí)過react,但是并沒有實踐經(jīng)驗

這個需求最終的效果是和石墨標(biāo)題修改實現(xiàn)一樣的效果

初始需求

  • 文案支持可編輯
  • 用戶點擊位置即光標(biāo)定位處
  • 超過50字讀的時候,超出部分進(jìn)行截斷
  • 當(dāng)用戶把所有內(nèi)容刪除時,失去焦點時文案設(shè)置為 “無文案”三個字
  • 編輯區(qū)域隨著編輯內(nèi)容的寬度而變化,最大寬度1000px 500px
  • 失去焦點時保存文案內(nèi)容

方案設(shè)計

在看到第一眼需求的時候,想到的時候用span和input進(jìn)行切換,但是這個肯定是滿足不了需求中第2點,所以首先這個需求肯定不會是兩個 標(biāo)簽切換,只能一個標(biāo)簽承擔(dān)展示和編輯的功能,第一反應(yīng)是用html屬性contentEditable,就有了我的第一個套方案,后因為需求的第三點實現(xiàn)上存在問題,所以被迫換了方案二(使用input標(biāo)簽),下面我們詳細(xì)說說為啥棄用方案1選用方案二以及在這過程中遇到的問題。

方案一 span + contentEditable

思路

  • 利用h5提供contentEditble,可實現(xiàn)需求點的1/2/5
  • 監(jiān)聽focus事件和input時間,可以實現(xiàn)需求點4
  • 監(jiān)聽blur事件,可以實現(xiàn)需求點3

但是 需求點中的3點,因為是用字?jǐn)?shù)做的截斷,在這個方案中是實現(xiàn)不了的,所以我給出的建議方案是編輯的時候不做截斷,非編輯的時候做截斷段(是否失去焦點可用作判斷是否為編輯態(tài)的依據(jù))

代碼如下

演示demo:

import React, { useState, useRef, useEffect } from 'react';
import ReactDom from 'react-dom';
interface EditTextProps {
  text: string;
  // 告知父組件文案已被修改
  changeText?: (text: string) => void;
}
const EditText = function (props: EditTextProps) {
  useEffect(() => {
    setShowText(props.text);
  }, [props.text]);
  const [showText, setShowText] = useState('');
  const [isBlank, setIsBlank] = useState(false);
  const [isFocus, setIsFocus] = useState(false);
  const textRef = useRef<HTMLDivElement>(null);
  const onFocus = () => {
    setIsFocus(true)
  }
  const onInput = () => {
    // 避免失去焦點的時候,標(biāo)題區(qū)域明顯的閃動
    setIsBlank(!textRef.current?.innerHTML);
  }
  const onBlur = () => {
    const newTitle = textRef.current?.innerHTML || '無標(biāo)題';
    const oldTitle = props.text;
    setIsFocus(false);
    setIsBlank(false);
    // 文案更新
    if (newTitle !== oldTitle) {
      props?.changeText(newTitle);
      setShowText(getCharsByLength(newTitle, 50));
    }
    else {
      // 文案不更新
      setShowText(getCharsByLength(newTitle, 50));
      if(textRef.current) {
        textRef.current.innerHTML = getCharsByLength(newTitle, 50)
      }
    }
  }
  // 獲取前l(fā)ength個字符
  const getCharsByLength = (title: string, length: number) => {
    const titleLength = title.length;
    // 假設(shè)都是非中文字符,一個中文字符的寬度可以顯示兩個非中文字符
    let maxLength = length * 2;
    const result = [];
    for (let i = 0; i < titleLength; i++) {
      const char = title[i];
      // 中文字符寬度2,非中文字符寬度1
      maxLength -= /[\u4e00-\u9fa5]/.test(char) ? 2 : 1;
      result.push(char);
      if (maxLength <= 0) {
        break;
      }
    }
    if (result.length < titleLength) {
      result.push('...');
    }
    return result.join('');
  };
  return <div className="title">
    {isFocus && isBlank ? <span className="title-blank">無標(biāo)題</span> : ''}
    <span
      className="title-text"
      contentEditable
      suppressContentEditableWarning
      ref={textRef}
      onFocus={onFocus}
      onInput={onInput}
      onBlur={onBlur}
    >{showText}</span>
  </div>;
};

在這個方案中遇到的問題

如果在用戶修改之前的文案就是【無標(biāo)題】,此時用戶刪除了文案所有的內(nèi)容【將文案置空】,此時失去焦點,根據(jù)需求我們應(yīng)該展示【無標(biāo)題】,可是在代碼邏輯中 進(jìn)行了setShowText(getCharsByLength(newTitle, 50));的處理,在不斷試探中,發(fā)現(xiàn)修改前后的showText一摸一樣,無法觸發(fā)dom的更新,針對這個問題我找到了兩個解決方式

  • 方式一 在不需要更新標(biāo)題,用戶觸發(fā)了失去焦點,但是并沒有修改標(biāo)題時,先把showText設(shè)置為空,在setTimeout中設(shè)置會以前的標(biāo)題。

嘗試了一下這個方案,從使用角度來說并不會特別明顯的閃動。不過個人覺得這個方案代碼看著很怪異

const onBlur = () => {
    const newTitle = textRef.current?.innerHTML || '無標(biāo)題';
    const oldTitle = props.text;
    setIsFocus(false);
    setIsBlank(false);
    // 文案更新
    if (newTitle !== oldTitle) {
      props?.changeText(newTitle);
      setShowText(getCharsByLength(newTitle, 50));
    }
    else {
      // 文案不更新
      setShowText('');
      setTimeout(() => {
        setShowText(getCharsByLength(newTitle, 50));
      }, 0)
    }
  }
  • 方式二 利用ref
const onBlur = () => {
    const newTitle = textRef.current?.innerHTML || '無標(biāo)題';
    const oldTitle = props.text;
    setIsFocus(false);
    setIsBlank(false);
    // 文案更新
    if (newTitle !== oldTitle) {
      props?.changeText(newTitle);
      setShowText(getCharsByLength(newTitle, 50));
    }
    else {
      // 文案不更新
      setShowText(getCharsByLength(newTitle, 50));
      if(textRef.current) {
        textRef.current.innerHTML = getCharsByLength(newTitle, 50)
      }
    }
  }

存在的問題

  • 無法用字?jǐn)?shù)做限制
  • 如果用寬度做限制,可以出現(xiàn)截斷的效果,但是內(nèi)容無法滑動

方案二 直接用input處理展示和編輯

采用修改input框樣式的方法,讓input展示和可編輯文案。整體的效果和文章開頭展示的效果一致。 canEdit這個參數(shù)時我后面加的,用來控制EditText組件是否可以編輯。遇到的問題見面后面。 演示demo:

import React, { useState, useEffect, useRef, useLayoutEffect } from 'react';
interface EditTextProps {
  text: string;
  canEdit?: boolean;
  changeText?: (text: string) => void;
}
function EditText(props: EditTextProps) {
  // 根據(jù)span獲取寬度
  const witdthRef = useRef<HTMLDivElement>(null);
  const [showText, setShowText] = useState('');
  const [isFocus, setIsFocus] = useState(false);
  const [inputWith, setInputWith] = useState(100);
  const minTitleWidth = 70;
  const maxTitleWidth = 500;
  useEffect(() => {
    setShowText(props.text);
  }, [props.text]);
  useLayoutEffect(() => {
    dealInputWidth();
  }, [showText]);
  const dealInputWidth = () => {
    const offsetWidth = witdthRef?.current?.offsetWidth || minTitleWidth;
    // +5 防止出現(xiàn) 截斷
    const width = offsetWidth < maxTitleWidth ? offsetWidth + 5 : maxTitleWidth;
    setInputWith(width);
  };
  const titleFocus = () => {
    setIsFocus(true);
  };
  const titleInput = (e: React.ChangeEvent<HTMLInputElement>) => {
    const newTitle = e.target.value;
    setShowText(newTitle);
  };
  const titleBlur = () => {
    const newTitle = showText || '無標(biāo)題';
    const oldTitle = props.text;
    setIsFocus(false);
    if (showText !== oldTitle) {
      setShowText(newTitle);
      setIsFocus(false);
      if (props?.changeText) {
        props.changeText(newTitle);
      }
    } else {
      setIsFocus(false);
      setShowText(newTitle);
    }
  };
  return (
      <div className='wrap'>
        {props.canEdit ? (
          <input
            value={showText}
            style={{ width: inputWith }}
            onFocus={titleFocus}
            onChange={titleInput}
            onBlur={titleBlur}
            className='input'
            placeholder="無標(biāo)題"
          />
        ) : (
          ''
        )}
        {/* 為了計算文字的寬度 */}
        <span ref={witdthRef} className={props.canEdit ? 'width' : 'text'}>
          {showText}
        </span>
      </div>
  );
}  

踩到的坑

input自帶寬度,無法實現(xiàn)寬度隨著文案的改變而改變。

在方案一做出來后,就和UI進(jìn)行了溝通在【編輯的時候用字?jǐn)?shù)做截斷實現(xiàn)不了】,給出了一個建議的方案【編輯的時候不做截斷】,但是設(shè)計同學(xué)覺得不截斷的方案過丑,,,,,然后她就說能實現(xiàn) 【石墨標(biāo)題編輯】時,類似的效果交互嗎???于是我就開啟了研究石墨的效果的征途中。

只發(fā)現(xiàn) 石墨用了一個input實現(xiàn)了不錯的效果,input后面放了一個span標(biāo)簽,我體驗的時候,一直在想為什么會有一個span標(biāo)簽?zāi)???(小朋友,是不是滿臉疑問)

直到我發(fā)現(xiàn)input自帶寬度,無法隨著內(nèi)容的寬度的改變而改變。此時才恍然大悟span標(biāo)簽的作用。

我也采用了利用span標(biāo)簽的寬度的方式來控input輸入內(nèi)容的寬度。
開玩笑,咋可能這么順利,我遇到了第二個問題

用useEffect 來監(jiān)控 witdthRef.current.offsetWidth時,拿到的是上次文案的寬度 經(jīng)過查閱資料,我發(fā)現(xiàn)了useLayoutEffect這個hook,真香

以上就是react編寫可編輯標(biāo)題示例詳解的詳細(xì)內(nèi)容,更多關(guān)于react編寫可編輯標(biāo)題的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • React路由動畫切換實現(xiàn)過程詳解

    React路由動畫切換實現(xiàn)過程詳解

    這篇文章主要介紹了react-router 路由切換動畫的實現(xiàn)示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2022-12-12
  • 使用Webpack打包React項目的流程步驟

    使用Webpack打包React項目的流程步驟

    隨著React應(yīng)用日益復(fù)雜,開發(fā)者需要借助模塊打包工具來管理項目依賴、轉(zhuǎn)換代碼和優(yōu)化性能,Webpack是一款功能強大的模塊打包器,它可以將React項目中的JavaScript、CSS、圖片等資源打包成瀏覽器友好的文件,本文將全面介紹如何使用Webpack打包React項目
    2025-03-03
  • antd-react使用Select組件defaultValue踩的坑及解決

    antd-react使用Select組件defaultValue踩的坑及解決

    這篇文章主要介紹了antd-react使用Select組件defaultValue踩的坑及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-05-05
  • React基礎(chǔ)-JSX的本質(zhì)-虛擬DOM的創(chuàng)建過程實例分析

    React基礎(chǔ)-JSX的本質(zhì)-虛擬DOM的創(chuàng)建過程實例分析

    這篇文章主要介紹了React基礎(chǔ)-JSX的本質(zhì)-虛擬DOM的創(chuàng)建過程,結(jié)合具體實例形式分析了虛擬dom的基本原理與實現(xiàn)方法,需要的朋友可以參考下
    2023-05-05
  • 詳解React中如何獲取真實的dom

    詳解React中如何獲取真實的dom

    這篇文章主要為大家詳細(xì)介紹了React中獲取真實的dom的相關(guān)方法,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-02-02
  • react-redux action傳參及多個state處理的實現(xiàn)

    react-redux action傳參及多個state處理的實現(xiàn)

    本文主要介紹了react-redux action傳參及多個state處理的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07
  • React受控組件和非受控組件對比詳解

    React受控組件和非受控組件對比詳解

    在使用React搭建用戶界面時,組件可以分為兩類:受控組件和費受控組件這兩種組件在數(shù)據(jù)處理方式上有著顯著的差異,理解它們的區(qū)別對于構(gòu)建高效和可控的React應(yīng)用至關(guān)重要,本文將深入探討這兩種組件的定義、特點以及使用場景,并通過示例代碼幫助你更加清晰地理解它們
    2025-01-01
  • React 組件間的通信示例

    React 組件間的通信示例

    這篇文章主要介紹了React 組件間的通信示例,主要通信劃分為三種,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • react封裝Dialog彈框的方法

    react封裝Dialog彈框的方法

    這篇文章主要為大家詳細(xì)介紹了react封裝Dialog彈框的方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-08-08
  • 關(guān)于React Native報Cannot initialize a parameter of type''NSArray<id<RCTBridgeModule>>錯誤(解決方案)

    關(guān)于React Native報Cannot initialize a parameter of type''NSArra

    這篇文章主要介紹了關(guān)于React Native報Cannot initialize a parameter of type'NSArray<id<RCTBridgeModule>>錯誤,本文給大家分享解決方案,需要的朋友可以參考下
    2021-05-05

最新評論

宣汉县| 论坛| 盐边县| 乐陵市| 南昌市| 伊金霍洛旗| 合川市| 托克托县| 耿马| 德庆县| 绥芬河市| 红桥区| 兴安县| 肇州县| 天全县| 清苑县| 九江县| 宁强县| 陵水| 虞城县| 阆中市| 榆林市| 台南市| 宜昌市| 年辖:市辖区| 大余县| 六盘水市| 高雄县| 西丰县| 勃利县| 莆田市| 鲁山县| 文成县| 新蔡县| 巴东县| 铜鼓县| 兰西县| 巴林左旗| 襄垣县| 白沙| 莎车县|