ब्राउज़र पर रेंडर का दृश्य रीऐक्ट के साथ आकार बदलता है


313

जब ब्राउज़र विंडो का आकार बदल दिया जाता है, तो मैं उस दृश्य को फिर से कैसे प्रस्तुत कर सकता हूं?

पृष्ठभूमि

मेरे पास कुछ ब्लॉक हैं जो मैं पृष्ठ पर व्यक्तिगत रूप से लेआउट करना चाहता हूं, हालांकि मैं यह भी चाहता हूं कि ब्राउज़र विंडो में परिवर्तन होने पर वे अपडेट करें। बहुत अंतिम परिणाम बेन हॉलैंड के Pinterest लेआउट की तरह कुछ होगा , लेकिन केवल रिएक्ट नहीं jQuery का उपयोग करते हुए लिखा गया है। मैं अभी भी एक रास्ता हूँ।

कोड

यहाँ मेरा ऐप है:

var MyApp = React.createClass({
  //does the http get from the server
  loadBlocksFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      mimeType: 'textPlain',
      success: function(data) {
        this.setState({data: data.events});
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentWillMount: function() {
    this.loadBlocksFromServer();

  },    
  render: function() {
    return (
        <div>
      <Blocks data={this.state.data}/>
      </div>
    );
  }
});

React.renderComponent(
  <MyApp url="url_here"/>,
  document.getElementById('view')
)

तब मेरे पास Blockघटक है ( Pinउपरोक्त Pinterest उदाहरण में बराबर ):

var Block = React.createClass({
  render: function() {
    return (
        <div class="dp-block" style={{left: this.props.top, top: this.props.left}}>
        <h2>{this.props.title}</h2>
        <p>{this.props.children}</p>
        </div>
    );
  }
});

और सूची / संग्रह Blocks:

var Blocks = React.createClass({

  render: function() {

    //I've temporarily got code that assigns a random position
    //See inside the function below...

    var blockNodes = this.props.data.map(function (block) {   
      //temporary random position
      var topOffset = Math.random() * $(window).width() + 'px'; 
      var leftOffset = Math.random() * $(window).height() + 'px'; 
      return <Block order={block.id} title={block.summary} left={leftOffset} top={topOffset}>{block.description}</Block>;
    });

    return (
        <div>{blockNodes}</div>
    );
  }
});

सवाल

क्या मुझे jQuery की विंडो का आकार बदलना चाहिए? यदि हां, तो कहां?

$( window ).resize(function() {
  // re-render the component
});

क्या ऐसा करने का एक और "रिएक्ट" तरीका है?

जवाबों:


531

रिएक्ट हुक का उपयोग करना:

आप एक कस्टम हुक को परिभाषित कर सकते हैं जो विंडो resizeईवेंट को सुनता है , कुछ इस तरह से:

import React, { useLayoutEffect, useState } from 'react';

function useWindowSize() {
  const [size, setSize] = useState([0, 0]);
  useLayoutEffect(() => {
    function updateSize() {
      setSize([window.innerWidth, window.innerHeight]);
    }
    window.addEventListener('resize', updateSize);
    updateSize();
    return () => window.removeEventListener('resize', updateSize);
  }, []);
  return size;
}

function ShowWindowDimensions(props) {
  const [width, height] = useWindowSize();
  return <span>Window size: {width} x {height}</span>;
}

यहां फायदा यह है कि लॉजिक एनकैप्सुलेटेड है, और आप इस हुक का उपयोग खिड़की के आकार का उपयोग करने के लिए कहीं भी कर सकते हैं।

प्रतिक्रिया वर्गों का उपयोग करना:

आप कंपोनेंटडिमाउंट में सुन सकते हैं, कुछ इस तरह का कंपोनेंट जो सिर्फ विंडो आयाम प्रदर्शित करता है (जैसे <span>Window size: 1024 x 768</span>):

import React from 'react';

class ShowWindowDimensions extends React.Component {
  state = { width: 0, height: 0 };
  render() {
    return <span>Window size: {this.state.width} x {this.state.height}</span>;
  }
  updateDimensions = () => {
    this.setState({ width: window.innerWidth, height: window.innerHeight });
  };
  componentDidMount() {
    window.addEventListener('resize', this.updateDimensions);
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.updateDimensions);
  }
}

5
यह कैसे काम करता है? this.updateDimensionsपारित कर दिया करने के लिए addEventListenerसिर्फ एक नंगे समारोह संदर्भ जिसके लिए कोई मूल्य नहीं होगा thisजब कहा जाता है। एक अनाम फ़ंक्शन, या एक .bind () कॉल को जोड़ने के लिए उपयोग किया जाना चाहिए this, या मुझे गलत समझा गया है?
फ़ेडबाई

24
@chrisdew मैं यहाँ थोड़ी देर से हूँ, लेकिन thisसीधे घटक पर परिभाषित किसी भी तरीके के लिए ऑटो-बाइंड करें ।
मिशेल टिली

3
@MattDell हाँ, ES6 कक्षाएं केवल सामान्य वर्ग हैं, इसलिए उनके साथ कोई ऑटो-बंधन नहीं है।
मिशेल टिली

30
कोई jQuery की जरूरत है - उपयोग innerHeightऔर innerWidthसे window। और अगर आप को छोड़ सकते हैं componentWillMount, तो आप का उपयोग getInitialStateनिर्धारित करने के लिए heightऔर width
sighrobot

2
जैसे @MattDell दिखता ::बाँध वाक्यविन्यास अब के लिए बाहर है sitepoint.com/bind-javascripts-this-keyword-react ( "बाँध ऑपरेटर: :) नहीं फरवरी में जम गया, ES7 का हिस्सा बनने के लिए जा रहा है के रूप में ES7 सेट सुविधाएँ , बाइंड ऑपरेटर ES8 के लिए एक प्रस्ताव है "
जारोड स्मिथ

130

@SophieAlpert सही है, +1, मैं सिर्फ इस उत्तर के आधार पर, jQuery के बिना , उसके समाधान का एक संशोधित संस्करण प्रदान करना चाहता हूं ।

var WindowDimensions = React.createClass({
    render: function() {
        return <span>{this.state.width} x {this.state.height}</span>;
    },
    updateDimensions: function() {

    var w = window,
        d = document,
        documentElement = d.documentElement,
        body = d.getElementsByTagName('body')[0],
        width = w.innerWidth || documentElement.clientWidth || body.clientWidth,
        height = w.innerHeight|| documentElement.clientHeight|| body.clientHeight;

        this.setState({width: width, height: height});
        // if you are using ES2015 I'm pretty sure you can do this: this.setState({width, height});
    },
    componentWillMount: function() {
        this.updateDimensions();
    },
    componentDidMount: function() {
        window.addEventListener("resize", this.updateDimensions);
    },
    componentWillUnmount: function() {
        window.removeEventListener("resize", this.updateDimensions);
    }
});

केवल तब काम करता है जब आपके पास आपका आईई-संबंधित पॉलीफ़िल्स वैनिला जेएस ईवेंट श्रोताओं के लिए सेट किया गया है
nnnn

@nnn क्या आप विस्तृत कर सकते हैं? मैं मानता हूं कि मैंने केवल क्रोम में इसका परीक्षण किया था। क्या आप कह रहे हैं कि window.addEventListener पॉलीफिल के बिना IE पर काम करने वाला नहीं है?
एंड्रे पेना

2
@andrerpena caniuse.com/#search=addeventlistener यानी 8 को समस्या होगी
nnnn

2
@nnnn। समझा। हां .. तो मेरा समाधान IE 8 पर काम नहीं करता है, लेकिन 9 से काम करता है :)। धन्यवाद।
एंड्रे पेना

35
क्या कोई वास्तव में IE8 के बारे में अभी भी परवाह करता है? या यह सिर्फ आदत है?
ठंढा पड़ने वाला

48

एक बहुत ही सरल उपाय:

resize = () => this.forceUpdate()

componentDidMount() {
  window.addEventListener('resize', this.resize)
}

componentWillUnmount() {
  window.removeEventListener('resize', this.resize)
}

12
बल अद्यतन को कुचलना मत भूलना या यह बहुत ही गड़बड़ दिखने वाला है।
k2snowman69 19

4
इसके अलावा श्रोता को हटाने के लिए मत भूलना componentWillUnmount()!
जैमर जोन्स

यह सबसे अच्छा उपाय है अगर इसे गला दिया जाए। मेरा मतलब है अगर forceUpdateसशर्त रूप से लागू किया जाता है
asmmahmud

1
यह जबरदस्ती नहीं है (जिस चीज को कहा जा रहा है) जिसे थ्रॉटल करने की आवश्यकता है, यह रिसाइज इवेंट फायरिंग है जिसे थ्रॉटल करने की जरूरत है (चीज फायरिंग)। आकार परिवर्तन की घटनाओं को तकनीकी रूप से हर पिक्सेल पर बुलाया जा सकता है क्योंकि आप बड़े से छोटे तक एक विंडो का आकार बदलते हैं। जब कोई उपयोगकर्ता जल्दी से ऐसा करता है, तो आपके द्वारा देखभाल की तुलना में अधिक घटनाएं होती हैं। इससे भी बदतर, आप यूआई थ्रेड को जावास्क्रिप्ट थ्रेड से बांध रहे हैं जिसका अर्थ है कि आपका ऐप एक गंभीर रूप से धीमा महसूस करना शुरू कर देगा क्योंकि यह प्रत्येक घटना को व्यक्तिगत रूप से संभालने की कोशिश करता है।
k2snowman69

1
हर पिक्सेल पर आकार बदलने वाले फ़ंक्शन को चलाने के बजाय, आप इसे तरलता का भ्रम देने के लिए कुछ कम आवधिक मात्रा में चलाते हैं, जिसे आप हमेशा पहली और अंतिम घटना को संभालते हैं और इस तरह यह महसूस करते हैं कि यह आकार बदलने वाला तरल पदार्थ है।
k2snowman69

42

यह jQuery के बिना es6 का उपयोग करने का एक सरल और छोटा उदाहरण है।

import React, { Component } from 'react';

export default class CreateContact extends Component {
  state = {
    windowHeight: undefined,
    windowWidth: undefined
  }

  handleResize = () => this.setState({
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth
  });

  componentDidMount() {
    this.handleResize();
    window.addEventListener('resize', this.handleResize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize)
  }

  render() {
    return (
      <span>
        {this.state.windowWidth} x {this.state.windowHeight}
      </span>
    );
  }
}

हुक

import React, { useEffect, useState } from "react";

let App = () => {
  const [windowWidth, setWindowWidth] = useState(0);
  const [windowHeight, setWindowHeight] = useState(0);
  let resizeWindow = () => {
    setWindowWidth(window.innerWidth);
    setWindowHeight(window.innerHeight);
  };

  useEffect(() => {
    resizeWindow();
    window.addEventListener("resize", resizeWindow);
    return () => window.removeEventListener("resize", resizeWindow);
  }, []);

  return (
    <div>
      <span>
        {windowWidth} x {windowHeight}
      </span>
    </div>
  );
};

4
यह एक अच्छा, संक्षिप्त जवाब है लेकिन AFAICT में एक बग है: जब तक मुझे गलत नहीं किया जाता है, ::बाइंड ऑपरेटर हर बार इसे लागू करने पर एक नया मूल्य देता है। इसलिए आपका ईवेंट श्रोता वास्तव में अपंजीकृत नहीं होगा, क्योंकि आपके removeEventListenerसिरों को मूल रूप से जो पारित किया गया था, उसकी तुलना में एक अलग फ़ंक्शन पास करना है addEventListener
natevw

20

प्रतिक्रिया के रूप में 16.8 आप हुक का उपयोग कर सकते हैं !

/* globals window */
import React, { useState, useEffect } from 'react'
import _debounce from 'lodash.debounce'

const Example = () => {
  const [width, setWidth] = useState(window.innerWidth)

  useEffect(() => {
    const handleResize = _debounce(() => setWidth(window.innerWidth), 100)

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    }
  }, [])

  return <>Width: {width}</>
}

13

संपादित करें 2018 : अब रिएक्ट में संदर्भ के लिए प्रथम श्रेणी का समर्थन है


मैं एक सामान्य जवाब देने की कोशिश करूंगा, जो इस विशिष्ट समस्या को लक्षित करता है लेकिन एक सामान्य समस्या भी है।

यदि आप दुष्प्रभावों के बारे में परवाह नहीं करते हैं, तो आप बस पैकेट जैसी किसी चीज का उपयोग कर सकते हैं

यदि आप फ्लक्स का उपयोग करते हैं, तो आप एक स्टोर बना सकते हैं जिसमें खिड़की के गुण होते हैं ताकि आप बिना खिड़की के ऑब्जेक्ट को क्वेरी किए बिना एक शुद्ध रेंडर फ़ंक्शन रख सकें।

ऐसे अन्य मामलों में जहां आप एक उत्तरदायी वेबसाइट बनाना चाहते हैं, लेकिन आप मीडिया के प्रश्नों के लिए रिएक्ट इनलाइन शैलियों को प्राथमिकता देते हैं, या चाहते हैं कि HTML / JS व्यवहार खिड़की की चौड़ाई के अनुसार बदल जाए, पढ़ते रहें:

प्रतिक्रियात्मक संदर्भ क्या है और मैं इसके बारे में क्यों बात करता हूं

प्रतिक्रिया प्रसंग सार्वजनिक API में नहीं है और सम्पूर्ण पदानुक्रम के गुणों को पास करने की अनुमति देता है।

प्रतिक्रिया संदर्भ विशेष रूप से आपकी संपूर्ण एप्लिकेशन चीजों को पास करने के लिए उपयोगी है जो कभी नहीं बदलती हैं (इसका उपयोग कई फ्लक्स फ्रेमवर्क द्वारा मिक्सिन के माध्यम से किया जाता है)। आप इसका उपयोग ऐप बिजनेस इनवेरिएंट्स (जैसे कनेक्टेड यूजरआईडी को स्टोर करने के लिए कर सकते हैं, ताकि यह हर जगह उपलब्ध हो)।

लेकिन इसका उपयोग उन चीजों को संग्रहीत करने के लिए भी किया जा सकता है जो बदल सकते हैं। समस्या यह है कि जब संदर्भ बदलता है, तो इसका उपयोग करने वाले सभी घटकों को फिर से प्रस्तुत किया जाना चाहिए और ऐसा करना आसान नहीं है, सबसे अच्छा समाधान अक्सर नए संदर्भ के साथ पूरे ऐप को अनमाउंट / रिमाउंट करना है। याद रखें कि बलवर्धक पुनरावर्ती नहीं है

इसलिए जैसा कि आप समझते हैं, संदर्भ व्यावहारिक है, लेकिन जब यह बदलता है तो प्रदर्शन प्रभाव पड़ता है, इसलिए इसे अक्सर बदलना नहीं चाहिए।

संदर्भ में क्या रखा जाए

  • Invariants: जुड़े हुए userId की तरह, sessionToken, जो भी ...
  • चीजें जो अक्सर बदलती नहीं हैं

यहां ऐसी चीजें हैं जो अक्सर बदलती नहीं हैं:

वर्तमान उपयोगकर्ता भाषा :

यह बहुत बार नहीं बदलता है, और जब यह होता है, जैसा कि पूरे ऐप का अनुवाद किया जाता है, हमें सब कुछ फिर से प्रस्तुत करना होगा: गर्म लैंगेज परिवर्तन का एक बहुत अच्छा उपयोग

खिड़की के गुण

चौड़ाई और ऊंचाई अक्सर बदलती नहीं है लेकिन जब हम अपना लेआउट और व्यवहार करते हैं तो उसे अनुकूलित करना पड़ सकता है। लेआउट के लिए कभी-कभी सीएसएस मीडियाक्वायरीज के साथ अनुकूलित करना आसान होता है, लेकिन कभी-कभी यह नहीं होता है और इसके लिए अलग HTML संरचना की आवश्यकता होती है। व्यवहार के लिए आपको इसे जावास्क्रिप्ट के साथ संभालना होगा।

आप हर आकार की घटना पर सब कुछ फिर से प्रस्तुत करना नहीं चाहते हैं, इसलिए आपको आकार बदलने वाली घटनाओं का टीकाकरण करना होगा।

मैं आपकी समस्या के बारे में समझता हूं कि आप स्क्रीन की चौड़ाई के अनुसार कितने आइटम प्रदर्शित करना चाहते हैं। इसलिए आपको सबसे पहले उत्तरदायी ब्रेकप्वाइंट को परिभाषित करना होगा , और आपके पास विभिन्न लेआउट प्रकारों की संख्या को गणना करना है।

उदाहरण के लिए:

  • लेआउट "1col", चौड़ाई के लिए <= 600
  • 600 "चौड़ाई <1000 के लिए लेआउट" 2col "
  • 1000 <= चौड़ाई के लिए लेआउट "3col"

आकार बदलने वाली घटनाओं (बहस) में, आप आसानी से विंडो ऑब्जेक्ट को क्वेरी करके वर्तमान लेआउट प्रकार प्राप्त कर सकते हैं।

फिर आप पूर्व लेआउट प्रकार के साथ लेआउट प्रकार की तुलना कर सकते हैं, और अगर यह बदल गया है, तो ऐप को एक नए संदर्भ के साथ फिर से प्रस्तुत करें: यह उपयोगकर्ता को पुन: रेंडर करने से बचने के लिए अनुमति देता है जब उपयोगकर्ता ने घटनाओं का आकार परिवर्तन किया है लेकिन वास्तव में लेआउट प्रकार परिवर्तित नहीं हुआ है, इसलिए आप केवल आवश्यकता होने पर पुनः प्रस्तुत करते हैं।

आपके पास एक बार, आप बस अपने ऐप के अंदर लेआउट प्रकार का उपयोग कर सकते हैं (संदर्भ के माध्यम से पहुंच योग्य) ताकि आप HTML, व्यवहार, सीएसएस कक्षाओं को अनुकूलित कर सकें ... आप रिएक्ट रेंडर फ़ंक्शन के अंदर अपने लेआउट प्रकार को जानते हैं तो इसका मतलब है कि आप इनलाइन शैलियों का उपयोग करके संवेदनशील वेबसाइटें सुरक्षित रूप से लिख सकते हैं, और मीडियाक्वेरीज़ की आवश्यकता नहीं है।

यदि आप फ्लक्स का उपयोग करते हैं, तो आप रिएक्ट संदर्भ के बजाय एक स्टोर का उपयोग कर सकते हैं, लेकिन यदि आपके ऐप में बहुत अधिक संवेदनशील घटक हैं, तो शायद संदर्भ का उपयोग करना आसान है?


10

मैं @senornestor समाधान का उपयोग करता हूं, लेकिन पूरी तरह से सही होने के लिए आपको इवेंट श्रोता को भी हटाना होगा:

componentDidMount() {
    window.addEventListener('resize', this.handleResize);
}

componentWillUnmount(){
    window.removeEventListener('resize', this.handleResize);
}

handleResize = () => {
    this.forceUpdate();
};

अन्यथा आपको चेतावनी मिल जाएगी:

चेतावनी: forceUpdate (...): केवल एक घुड़सवार या बढ़ते घटक को अपडेट कर सकता है। इसका आमतौर पर मतलब है कि आपने एक अनमाउंट घटक पर ForceUpdate () कहा है। यह एक नो-ऑप है। कृपया XXX घटक के लिए कोड देखें।


1
आपको एक कारण के लिए चेतावनी मिल रही है: आप जो कर रहे हैं वह खराब अभ्यास है। आपका रेंडर () फंक्शन प्रॉपर और स्टेट का शुद्ध फंक्शन होना चाहिए। आपके मामले में आपको राज्य में नए आकार को सहेजना चाहिए।
ज़ोरान404

7

मैं उपरोक्त सभी उत्तरों को छोड़ दूंगा और react-dimensionsउच्चतर आदेश घटक का उपयोग करना शुरू करूंगा ।

https://github.com/digidem/react-dimensions

सिर्फ एक सरल जोड़ने importऔर एक समारोह कॉल, और आप उपयोग कर सकते हैं this.props.containerWidthऔर this.props.containerHeightअपने घटक में।

// Example using ES6 syntax
import React from 'react'
import Dimensions from 'react-dimensions'

class MyComponent extends React.Component {
  render() (
    <div
      containerWidth={this.props.containerWidth}
      containerHeight={this.props.containerHeight}
    >
    </div>
  )
}

export default Dimensions()(MyComponent) // Enhanced component

1
यह खिड़की का आकार नहीं देता है, बस कंटेनर।
mjtamlyn

3
हाँ, यह पूरी बात है। खिड़की का आकार खोजने के लिए तुच्छ है। एक कंटेनर का आकार खोजने के लिए बहुत कठिन है और एक प्रतिक्रिया घटक के लिए बहुत अधिक उपयोगी है। react-dimensionsयहां तक ​​कि नवीनतम संस्करण प्रोग्रामेटिक रूप से परिवर्तित आयामों के लिए काम करता है (जैसे, एक div का आकार बदल गया, जो आपके कंटेनर के आकार को प्रभावित करता है, इसलिए आपको अपना आकार अपडेट करने की आवश्यकता है)। बेन अल्परट का जवाब केवल ब्राउज़र विंडो के आकार बदलने में मदद करता है। यहाँ देखें: github.com/digidem/react-dimensions/issues/4
MindJuice

1
react-dimensionsजावास्क्रिप्ट आकार बदलने के कारण विंडो के आकार में बदलाव, फ्लेक्सबॉक्स लेआउट में बदलाव और परिवर्तन होते हैं। मेरा मानना ​​है कि वह इसे कवर करता है। क्या आपके पास एक उदाहरण है कि यह ठीक से संभाल नहीं करता है?
माइंडजुइस

2
विंडो का आकार प्राप्त करने के लिए तुच्छ है। इसके लिए किसी पुस्तकालय की आवश्यकता नहीं है: window.innerWidth, window.innerHeightreact-dimensionsसमस्या के अधिक महत्वपूर्ण हिस्से को हल करता है, और विंडो के आकार बदलने पर (साथ ही जब कंटेनर बदलता है) आपके लेआउट कोड को भी ट्रिगर करता है।
माइंडजुइस

1
यह परियोजना अब सक्रिय रूप से बनाए नहीं रखी जा रही है।
परवलोर्ड

7

यह कोड नए रिएक्ट संदर्भ API का उपयोग कर रहा है :

  import React, { PureComponent, createContext } from 'react';

  const { Provider, Consumer } = createContext({ width: 0, height: 0 });

  class WindowProvider extends PureComponent {
    state = this.getDimensions();

    componentDidMount() {
      window.addEventListener('resize', this.updateDimensions);
    }

    componentWillUnmount() {
      window.removeEventListener('resize', this.updateDimensions);
    }

    getDimensions() {
      const w = window;
      const d = document;
      const documentElement = d.documentElement;
      const body = d.getElementsByTagName('body')[0];
      const width = w.innerWidth || documentElement.clientWidth || body.clientWidth;
      const height = w.innerHeight || documentElement.clientHeight || body.clientHeight;

      return { width, height };
    }

    updateDimensions = () => {
      this.setState(this.getDimensions());
    };

    render() {
      return <Provider value={this.state}>{this.props.children}</Provider>;
    }
  }

फिर आप इसे अपने कोड में इस तरह से जहाँ चाहे उपयोग कर सकते हैं:

<WindowConsumer>
  {({ width, height }) =>  //do what you want}
</WindowConsumer>

6

जरूरी नहीं कि आप फिर से रेंडर करने के लिए मजबूर करें।

यह मदद ओपी नहीं हो सकता है, लेकिन मेरे मामले में मैं केवल अद्यतन करने के लिए की जरूरत है widthऔर heightमेरी कैनवास पर गुण (जो आप सीएसएस के साथ नहीं कर सकते हैं)।

यह इस तरह दिख रहा है:

import React from 'react';
import styled from 'styled-components';
import {throttle} from 'lodash';

class Canvas extends React.Component {

    componentDidMount() {
        window.addEventListener('resize', this.resize);
        this.resize();
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.resize);
    }

    resize = throttle(() => {
        this.canvas.width = this.canvas.parentNode.clientWidth;
        this.canvas.height = this.canvas.parentNode.clientHeight;
    },50)

    setRef = node => {
        this.canvas = node;
    }

    render() {
        return <canvas className={this.props.className} ref={this.setRef} />;
    }
}

export default styled(Canvas)`
   cursor: crosshair;
`

5

सुनिश्चित नहीं है कि यह सबसे अच्छा तरीका है, लेकिन मेरे लिए जो काम किया गया था, वह पहली बार एक स्टोर बना रहा था, मैंने इसे विंडोस्टोर कहा:

import {assign, events} from '../../libs';
import Dispatcher from '../dispatcher';
import Constants from '../constants';

let CHANGE_EVENT = 'change';
let defaults = () => {
    return {
        name: 'window',
        width: undefined,
        height: undefined,
        bps: {
            1: 400,
            2: 600,
            3: 800,
            4: 1000,
            5: 1200,
            6: 1400
        }
    };
};
let save = function(object, key, value) {
    // Save within storage
    if(object) {
        object[key] = value;
    }

    // Persist to local storage
    sessionStorage[storage.name] = JSON.stringify(storage);
};
let storage;

let Store = assign({}, events.EventEmitter.prototype, {
    addChangeListener: function(callback) {
        this.on(CHANGE_EVENT, callback);
        window.addEventListener('resize', () => {
            this.updateDimensions();
            this.emitChange();
        });
    },
    emitChange: function() {
        this.emit(CHANGE_EVENT);
    },
    get: function(keys) {
        let value = storage;

        for(let key in keys) {
            value = value[keys[key]];
        }

        return value;
    },
    initialize: function() {
        // Set defaults
        storage = defaults();
        save();
        this.updateDimensions();
    },
    removeChangeListener: function(callback) {
        this.removeListener(CHANGE_EVENT, callback);
        window.removeEventListener('resize', () => {
            this.updateDimensions();
            this.emitChange();
        });
    },
    updateDimensions: function() {
        storage.width =
            window.innerWidth ||
            document.documentElement.clientWidth ||
            document.body.clientWidth;
        storage.height =
            window.innerHeight ||
            document.documentElement.clientHeight ||
            document.body.clientHeight;
        save();
    }
});

export default Store;

फिर मैंने उस स्टोर को अपने कंपोनेंट्स में इस्तेमाल किया, थोड़े इस तरह से:

import WindowStore from '../stores/window';

let getState = () => {
    return {
        windowWidth: WindowStore.get(['width']),
        windowBps: WindowStore.get(['bps'])
    };
};

export default React.createClass(assign({}, base, {
    getInitialState: function() {
        WindowStore.initialize();

        return getState();
    },
    componentDidMount: function() {
        WindowStore.addChangeListener(this._onChange);
    },
    componentWillUnmount: function() {
        WindowStore.removeChangeListener(this._onChange);
    },
    render: function() {
        if(this.state.windowWidth < this.state.windowBps[2] - 1) {
            // do something
        }

        // return
        return something;
    },
    _onChange: function() {
        this.setState(getState());
    }
}));

FYI करें, इन फ़ाइलों को आंशिक रूप से छंटनी की गई थी।


1
स्टोर राज्य को एक प्रेषण कार्रवाई के जवाब में केवल बदलना चाहिए। यह निश्चित रूप से ऐसा करने का एक अच्छा तरीका नहीं है।
ठंढा

2
@frostymarvelous वास्तव में, शायद बेहतर घटक में अन्य उत्तरों के रूप में स्थानांतरित हो गया।
डेविड सिंक्लेयर

@DavidSinclair या इससे भी बेहतर, onresizeएक प्रेषण कर सकता है WindowResizedAction
जॉन वीज़

1
यह ओवरकिल है।
जेसन राइस

5

मुझे पता है कि यह उत्तर दिया गया है, लेकिन अभी मैंने सोचा था कि मैं अपने समाधान को शीर्ष उत्तर के रूप में साझा करूंगा, हालांकि महान, अब थोड़ा पुराना हो सकता है।

    constructor (props) {
      super(props)

      this.state = { width: '0', height: '0' }

      this.initUpdateWindowDimensions = this.updateWindowDimensions.bind(this)
      this.updateWindowDimensions = debounce(this.updateWindowDimensions.bind(this), 200)
    }

    componentDidMount () {
      this.initUpdateWindowDimensions()
      window.addEventListener('resize', this.updateWindowDimensions)
    }

    componentWillUnmount () {
      window.removeEventListener('resize', this.updateWindowDimensions)
    }

    updateWindowDimensions () {
      this.setState({ width: window.innerWidth, height: window.innerHeight })
    }

केवल अंतर वास्तव में यह है कि मैं डिबगिंग कर रहा हूं (केवल प्रत्येक 200ms चल रहा है) अपडेटविंडोविम्यूशंस को आकार बदलने के लिए, प्रदर्शन को थोड़ा बढ़ाने के लिए, लेकिन जब यह ComponentDidMount पर कॉल किया जाता है, तो इसे डिबेट न करें।

मुझे यह पता चल रहा था कि कभी-कभी अगर आपके पास ऐसी स्थिति है, जहां यह अक्सर बढ़ रहा है, तो इसे माउंट करने के लिए यह काफी सुस्त था।

बस एक मामूली अनुकूलन लेकिन आशा है कि यह किसी की मदद करता है!


initUpdateWindowDimensionsविधि की परिभाषा कहाँ है ?
मैट

यह कंस्ट्रक्टर में परिभाषित किया गया है :) यह सिर्फ अपडेट हैव्यूडोमेडिमेंट्स घटक से बंधा हुआ है लेकिन बिना डिब्यू के।
मैट विल्स

3

बस उपयोग करने के लिए @ senornestor समाधान पर सुधार करने के लिए forceUpdateऔर resizeघटक पर इवेंट श्रोता को हटाने के लिए @ gkri का समाधान :

  1. आकार बदलने (या बहस) को आकार बदलने के लिए मत भूलना
  2. bind(this)कंस्ट्रक्टर में सुनिश्चित करें
import React from 'react'
import { throttle } from 'lodash'

class Foo extends React.Component {
  constructor(props) {
    super(props)
    this.resize = throttle(this.resize.bind(this), 100)
  }

  resize = () => this.forceUpdate()

  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }

  render() {
    return (
      <div>{window.innerWidth} x {window.innerHeight}</div>
    )
  }
}

एक और तरीका यह है कि केवल "डमी" स्थिति का उपयोग करें forceUpdate:

import React from 'react'
import { throttle } from 'lodash'

class Foo extends React.Component {
  constructor(props) {
    super(props)
    this.state = { foo: 1 }
    this.resize = throttle(this.resize.bind(this), 100)
  }

  resize = () => this.setState({ foo: 1 })

  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }

  render() {
    return (
      <div>{window.innerWidth} x {window.innerHeight}</div>
    )
  }
}

2
componentDidMount() {

    // Handle resize
    window.addEventListener('resize', this.handleResize);
}




handleResize = () => {
    this.renderer.setSize(this.mount.clientWidth, this.mount.clientHeight);
    this.camera.aspect = this.mount.clientWidth / this.mount.clientHeight;
    this.camera.updateProjectionMatrix();
};

केवल आकार परिवर्तन समारोह को परिभाषित करने की आवश्यकता है।

फिर रेंडरर्स आकार (कैनवास) को अपडेट करें, कैमरे के लिए एक नया पहलू अनुपात असाइन करें।

बेहिसाब और रिमूव करना मेरी राय में एक पागल समाधान है ...।

यदि आवश्यक हो तो नीचे माउंट है।

            <div
                className={this.state.canvasActive ? 'canvasContainer isActive' : 'canvasContainer'}
                ref={mount => {
                    this.mount = mount;
                }}
            />

2

मैं बस का उपयोग कर पाया इस बहुत अच्छी बात साझा करना चाहता था window.matchMedia

const mq = window.matchMedia('(max-width: 768px)');

  useEffect(() => {
    // initial check to toggle something on or off
    toggle();

    // returns true when window is <= 768px
    mq.addListener(toggle);

    // unmount cleanup handler
    return () => mq.removeListener(toggle);
  }, []);

  // toggle something based on matchMedia event
  const toggle = () => {
    if (mq.matches) {
      // do something here
    } else {
      // do something here
    }
  };

.matches सही या गलत वापस आएगा यदि विंडो निर्दिष्ट अधिकतम-चौड़ाई मान से अधिक या कम है, तो इसका मतलब है कि श्रोता को थ्रॉटल करने की कोई आवश्यकता नहीं है, क्योंकि माचिस की डिब्बी केवल एक बार जब बूलियन बदलती है।

useStateबुलियन मैचमीडिया रिटर्न को बचाने के लिए मेरे कोड को आसानी से समायोजित किया जा सकता है , और इसका उपयोग एक घटक, अग्नि सुरक्षा आदि को सशर्त रूप से प्रस्तुत करने के लिए किया जाता है।


1

इसे क्लास सिंटेक्स के साथ काम करने के लिए इसे कंस्ट्रक्टर में 'इस' के साथ बाँधना था

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.resize = this.resize.bind(this)      
  }
  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }
}

1

जवाब के लिए आप सभी का धन्यवाद। यहाँ मेरा React + Recompose है । यह एक उच्च क्रम फ़ंक्शन है जिसमें घटक के गुण windowHeightऔर windowWidthगुण शामिल हैं।

const withDimensions = compose(
 withStateHandlers(
 ({
   windowHeight,
   windowWidth
 }) => ({
   windowHeight: window.innerHeight,
   windowWidth: window.innerWidth
 }), {
  handleResize: () => () => ({
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth
  })
 }),
 lifecycle({
   componentDidMount() {
   window.addEventListener('resize', this.props.handleResize);
 },
 componentWillUnmount() {
  window.removeEventListener('resize');
 }})
)

1

https://github.com/renatorib/react-sizes अच्छा प्रदर्शन बनाए रखते हुए ऐसा करने के लिए एक HOC है।

import React from 'react'
import withSizes from 'react-sizes'

@withSizes(({ width }) => ({ isMobile: width < 480 }))
class MyComponent extends Component {
  render() {
    return <div>{this.props.isMobile ? 'Is Mobile' : 'Is Not Mobile'}</div>
  }
}

export default MyComponent

0

इस कारण से बेहतर है कि यदि आप CSS या JSON फ़ाइल डेटा से इस डेटा का उपयोग करते हैं, और फिर इस डेटा के साथ इस स्थिति के साथ नई स्थिति सेट करते हैं ।state ({चौड़ाई: "कुछ मूल्य", ऊंचाई: "कुछ मूल्य"}); या लेखन कोड जो स्वयं कार्य में चौड़ाई स्क्रीन डेटा का उपयोग करते हैं यदि आप उत्तरदायी शो छवियों को चाहते हैं

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.