React之卡片拖拽移動

管理頁面我們通常能看到一連串的卡片伶椿,以很明顯的方式對用戶進行提示月褥,也可以是圖表嵌在卡片的形式直觀展示等浊。那就會存在一個需求欲诺,用戶可以自定義自己想要的卡片布局抄谐,其實也就是自定義寬高這樣。

image.png

比如說上圖扰法,我想自定義成為下面這樣蛹含,
image.png

這樣一個需求我們?nèi)绾稳崿F(xiàn)呢?
查閱了一些文章之后塞颁,我選擇了React-Grid-Layout這樣一個組件浦箱,基本放大縮小以及拖拽這些都能實現(xiàn)卧斟,參照官方文檔就ok,我這里主要講一下憎茂,使用之后會出現(xiàn)的一些問題珍语。
因為是基于hzero的項目,所有的路由是像一個tab頁一樣并列在頂部竖幔,并且路由之間的跳轉(zhuǎn)不會觸發(fā)組件的unmount方法板乙,這時候就遇到了一個問題。當(dāng)我們在其他頁面進行瀏覽器的放大和縮小后拳氢,再回到使用React-Grid-Layout的頁面募逞,就會發(fā)現(xiàn),所有的卡片擠在一起馋评,樣式錯亂了放接,只在使用React-Grid-Layout的頁面縮放就不會有問題,這樣試了幾次之后留特,很奇怪纠脾,為什么出現(xiàn)這種情況呢,那就只能去看看組件源碼了蜕青,看了會苟蹈,我發(fā)現(xiàn)了問題。

// @flow
import React from "react";
import PropTypes from "prop-types";
import ReactDOM from "react-dom";
import type { ComponentType as ReactComponentType } from "react";

type WPProps = {
  className?: string,
  measureBeforeMount: boolean,
  style?: Object
};

type WPState = {
  width: number
};

/*
 * A simple HOC that provides facility for listening to container resizes.
 */
// typescript  類型檢查
export default function WidthProvider<
  Props,
  ComposedProps: { ...Props, ...WPProps }
>(
  ComposedComponent: ReactComponentType<Props>
): ReactComponentType<ComposedProps> {
  return class WidthProvider extends React.Component<ComposedProps, WPState> {
    static defaultProps = {
      measureBeforeMount: false
    };

    static propTypes = {
      // If true, will not render children until mounted. Useful for getting the exact width before
      // rendering, to prevent any unsightly resizing.
      measureBeforeMount: PropTypes.bool
    };

    state = {
      width: 1280
    };

    mounted: boolean = false;

    componentDidMount() {
      this.mounted = true;

      window.addEventListener("resize", this.onWindowResize);
      // Call to properly set the breakpoint and resize the elements.
      // Note that if you're doing a full-width element, this can get a little wonky if a scrollbar
      // appears because of the grid. In that case, fire your own resize event, or set `overflow: scroll` on your body.
      this.onWindowResize();
    }

    componentWillUnmount() {
      this.mounted = false;
      window.removeEventListener("resize", this.onWindowResize);
    }

    onWindowResize = () => {
      if (!this.mounted) return;
      // eslint-disable-next-line
      const node = ReactDOM.findDOMNode(this); // Flow casts this to Text | Element
      if (node instanceof HTMLElement)
        this.setState({ width: node.offsetWidth });
    };

    render() {
      const { measureBeforeMount, ...rest } = this.props;
      if (measureBeforeMount && !this.mounted) {
        return (
          <div className={this.props.className} style={this.props.style} />
        );
      }

      return <ComposedComponent {...rest} {...this.state} />;
    }
  };
}

可以看到這里監(jiān)聽了頁面的resize右核,然后進行onWindowResize方法慧脱,我們在其他頁面進行resize之后,也會觸發(fā)這個方法贺喝,這也是我前面要強調(diào)一下路由切換不會unmount的原因菱鸥,這里的resize沒法得到正確的node.offsetWidth,所以樣式錯亂了躏鱼,那怎么辦呢氮采?

首先我想著,在這個頁面寫一個時鐘挠他,不停的js 去觸發(fā)resize事件扳抽,讓它在切換頁面的時候重新計算篡帕,這樣肯定是ok的殖侵,但是這樣肯定會有性能問題,因為不停的延時調(diào)用镰烧,這樣我就得想個辦法拢军,不那么頻繁的刷新了,怎么做呢?

路由切換怔鳖,頁面也切換了茉唉,那組件肯定會觸發(fā)receiveProps鉤子,那我在這里判斷一下當(dāng)前頁面的路由,如果是當(dāng)前路由度陆,那就刷新一下(dispatchEvent('resize'))艾凯,這樣就做到只是進入這個頁面就刷新,比時鐘要好一些懂傀。但是這樣還是很low趾诗,因為進入其他頁面沒有resize操作,只要返回這個頁面就會刷新蹬蚁,接下來我就要優(yōu)化一下恃泪,只有當(dāng)其他頁面resize了之后,回到當(dāng)前頁才dispatchEvent('resize')犀斋。

// 當(dāng)切換到其他頁面并進行了頁面縮放之后重新resize當(dāng)前頁面
resize = debounce(() => {
  window.dispatchEvent(new Event('resize'));
  this.setState({
    loading: false,
    ifResize: false,
  });
}, 1000);

// eslint-disable-next-line
UNSAFE_componentWillReceiveProps() {
  if (window.location.href.indexOf('hipsappsa/pandect') > -1 && this.state.ifResize) {
    this.setState({
      loading: true,
    });
    this.resize();
  }
}

componentDidMount() {
  const callback = this.onreSize;
  const resize = debounce(() => {
    callback();
  }, 1000);
  window.addEventListener('resize', resize);
}

onResize() {
  const callback = this.onreSize;
  debounce(() => {
    callback();
  }, 1000);
}

/**
 * 響應(yīng)式瀑布
 */
@Bind
onreSize() {
  if (window.location.href.indexOf('hipsappsa/pandect') === -1) {
    this.setState({
      ifResize: true,
    });
  } else {
    this.setState({
      ifResize: false,
    });
  }
}

頁面掛載的時候就進行resize監(jiān)聽贝乎,當(dāng)發(fā)生resize事件之后進行一下路由判斷,如果是其他頁面叽粹,就改變state里的一個值览效,返回到當(dāng)前頁之后進行一下state的值判斷,為true才刷新虫几。

為了頁面切換的時候更合理一些(不展示錯亂的頁面)朽肥,加個loading延時一秒過度一下,還要記得unmount時去除掉resize監(jiān)聽持钉。

這里的圖表用的是bizcharts衡招,它會監(jiān)聽頁面resize的變化,但是當(dāng)我們改變寬高的時候每强,發(fā)現(xiàn)沒有自適應(yīng)始腾,一開始我的解決方法是把這個渲染的canvas設(shè)置為width:100%,這樣能使它拉伸開空执,但是很丑浪箭,因為是相當(dāng)于是放大,這樣效果太不好了辨绊,所以也要解決這個問題奶栖。

React-Grid-Layout組件有一個propsonResize事件返回當(dāng)前改變寬高這樣的事件回調(diào)门坷,我們在這個回調(diào)里手動觸發(fā)一下頁面resize事件宣鄙,以實現(xiàn)bizcharts的自適應(yīng)。

 // 當(dāng)手動修改resize之后觸發(fā)一次頁面resize使得bizcharts重新渲染
  handResize = debounce(() => {
    window.dispatchEvent(new Event('resize'));
  }, 100);

文章鏈接:https://www.npmjs.com/package/react-grid-layout

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末默蚌,一起剝皮案震驚了整個濱河市冻晤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌绸吸,老刑警劉巖鼻弧,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件设江,死亡現(xiàn)場離奇詭異,居然都是意外死亡攘轩,警方通過查閱死者的電腦和手機叉存,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來度帮,“玉大人鹉胖,你說我怎么就攤上這事」话” “怎么了甫菠?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長冕屯。 經(jīng)常有香客問我寂诱,道長,這世上最難降的妖魔是什么安聘? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任痰洒,我火速辦了婚禮,結(jié)果婚禮上浴韭,老公的妹妹穿的比我還像新娘丘喻。我一直安慰自己,他們只是感情好念颈,可當(dāng)我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布泉粉。 她就那樣靜靜地躺著,像睡著了一般榴芳。 火紅的嫁衣襯著肌膚如雪嗡靡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天窟感,我揣著相機與錄音讨彼,去河邊找鬼。 笑死柿祈,一個胖子當(dāng)著我的面吹牛哈误,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播躏嚎,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼蜜自,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了紧索?” 一聲冷哼從身側(cè)響起袁辈,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤菜谣,失蹤者是張志新(化名)和其女友劉穎珠漂,沒想到半個月后晚缩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡媳危,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年荞彼,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片待笑。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡鸣皂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出暮蹂,到底是詐尸還是另有隱情寞缝,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布仰泻,位于F島的核電站荆陆,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏集侯。R本人自食惡果不足惜被啼,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棠枉。 院中可真熱鬧浓体,春花似錦、人聲如沸辈讶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽贱除。三九已至咳促,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間勘伺,已是汗流浹背跪腹。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留飞醉,地道東北人冲茸。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像缅帘,于是被迫代替她去往敵國和親轴术。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

  • ??JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的。 ??事件失暂,就是文檔或瀏覽器窗口中發(fā)生的一些特...
    霜天曉閱讀 3,502評論 1 11
  • feisky云計算彼宠、虛擬化與Linux技術(shù)筆記posts - 1014, comments - 298, trac...
    不排版閱讀 3,867評論 0 5
  • JavaScript 與 HTML 之間的交互是通過事件實現(xiàn)的鳄虱。事件,就是文檔或瀏覽器窗口中發(fā)生的一些特定的交互瞬...
    LemonnYan閱讀 684評論 0 4
  • 本節(jié)介紹各種常見的瀏覽器事件凭峡。 鼠標(biāo)事件 鼠標(biāo)事件指與鼠標(biāo)相關(guān)的事件拙已,主要有以下一些。 click 事件摧冀,dblc...
    許先生__閱讀 2,446評論 0 4
  • 《人和人類》 奇怪的夜 兩個月亮的世界 里面的人 難以入眠 外面的我 也難以入眠 莫非我和他們之間 有了一種什么感...
    劉濤濤露西閱讀 295評論 0 0