मैं प्रतिक्रिया में ऑटो-आकार डोम तत्व की चौड़ाई का जवाब कैसे दे सकता हूं?


86

मेरे पास एक जटिल वेब पेज है जो रिएक्ट घटकों का उपयोग कर रहा है, और मैं पृष्ठ को स्थिर लेआउट से अधिक उत्तरदायी, पुनरुत्थानित लेआउट में बदलने की कोशिश कर रहा हूं। हालाँकि, मैं प्रतिक्रिया के साथ सीमाओं में चलता रहता हूं, और सोच रहा हूं कि क्या इन मुद्दों से निपटने के लिए कोई मानक पैटर्न है। मेरे विशिष्ट मामले में, मेरे पास एक घटक है जो डिस्प्ले के साथ डिव के रूप में प्रस्तुत होता है: टेबल-सेल और चौड़ाई: ऑटो।

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

इसके अलावा, जब विंडो आकार बदलती है, तो मैं सेटअप के दौरान एक घटक से दूसरे में आकार में परिवर्तन कैसे संवाद करूं? हम अपने सभी 3-पार्टी SVG रेंडरिंग को shouldComponentUpdate में कर रहे हैं, लेकिन आप उस विधि के भीतर स्वयं या अन्य चाइल्ड घटकों पर स्थिति या गुण सेट नहीं कर सकते।

प्रतिक्रिया का उपयोग करके इस समस्या से निपटने का एक मानक तरीका है?


1
वैसे, क्या आप सुनिश्चित हैं कि shouldComponentUpdateSVG रेंडर करने के लिए सबसे अच्छी जगह है? ऐसा लगता है कि आप क्या चाहते हैं componentWillReceivePropsया componentWillUpdateनहीं render
एंडी

1
यह आप के लिए देख रहे हैं या नहीं हो सकता है, लेकिन इसके लिए एक उत्कृष्ट पुस्तकालय है: github.com/bvaughn/react-virtualized AutoSizer घटक पर एक नज़र है। यह स्वचालित रूप से चौड़ाई और / या ऊंचाई का प्रबंधन करता है, इसलिए आपको नहीं करना है।
मैगी

@ मैगी की जाँच करें github.com/soupors रहस्यमय/react-measure भी, यह इस उद्देश्य के लिए एक स्टैंडअलोन लाइब्रेरी है, और अन्य अप्रयुक्त सामान को आपके ग्राहक बंडल में नहीं डालेगा ।
एंडी

हे, मैंने यहां एक समान प्रश्न का उत्तर दिया, यह किसी तरह से एक अलग दृष्टिकोण है और यह तय करते हैं कि आप अपने स्क्रेन प्रकार (मोबाइल, टैबलेट, डेस्कटॉप) के आधार पर क्या प्रस्तुत करना चाहते हैं
कैलिन ऑर्टन

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

जवाबों:


65

सबसे व्यावहारिक समाधान इसके लिए एक पुस्तकालय का उपयोग करना है जैसे प्रतिक्रिया-माप

अद्यतन : अब पता लगाने के लिए एक कस्टम हुक है (जिसे मैंने व्यक्तिगत रूप से आज़माया नहीं है): प्रतिक्रिया-आकार-पता । कस्टम हुक होने के कारण, यह उपयोग करने के लिए अधिक सुविधाजनक लगता है react-measure

import * as React from 'react'
import Measure from 'react-measure'

const MeasuredComp = () => (
  <Measure bounds>
    {({ measureRef, contentRect: { bounds: { width }} }) => (
      <div ref={measureRef}>My width is {width}</div>
    )}
  </Measure>
)

घटकों के बीच आकार में परिवर्तन को संप्रेषित करने के लिए, आप onResizeकॉलबैक पास कर सकते हैं और उन मूल्यों को संग्रहीत कर सकते हैं जो इसे कहीं मिलता है (इन दिनों राज्य को साझा करने का मानक तरीका Redux का उपयोग करना है ):

import * as React from 'react'
import Measure from 'react-measure'
import { useSelector, useDispatch } from 'react-redux'
import { setMyCompWidth } from './actions' // some action that stores width in somewhere in redux state

export default function MyComp(props) {
  const width = useSelector(state => state.myCompWidth) 
  const dispatch = useDispatch()
  const handleResize = React.useCallback(
    (({ contentRect })) => dispatch(setMyCompWidth(contentRect.bounds.width)),
    [dispatch]
  )

  return (
    <Measure bounds onResize={handleResize}>
      {({ measureRef }) => (
        <div ref={measureRef}>MyComp width is {width}</div>
      )}
    </Measure>
  )
}

यदि आप वास्तव में पसंद करना चाहते हैं तो अपना खुद का रोल कैसे करें:

एक आवरण घटक बनाएँ जो डोम से मान प्राप्त कर रहा है और विंडो आकार बदलने वाली घटनाओं को सुन रहा है (या घटक जैसा पता लगाता है react-measure)। आप इसे बताएं कि कौन सा प्रॉप डोम से प्राप्त करता है और एक रेंडर फ़ंक्शन को एक बच्चे के रूप में ले रहा है।

DOM प्रॉप्स पढ़े जाने से पहले आपको जो प्रस्तुत करना होता है उसे माउंट किया जाना चाहिए; जब वे प्रॉप्स शुरुआती रेंडर के दौरान उपलब्ध नहीं होते हैं, तो आप उपयोग करना चाह सकते हैं style={{visibility: 'hidden'}}ताकि उपयोगकर्ता इसे JS-कंप्यूटेड लेआउट मिलने से पहले नहीं देख सके।

// @flow

import React, {Component} from 'react';
import shallowEqual from 'shallowequal';
import throttle from 'lodash.throttle';

type DefaultProps = {
  component: ReactClass<any>,
};

type Props = {
  domProps?: Array<string>,
  computedStyleProps?: Array<string>,
  children: (state: State) => ?React.Element<any>,
  component: ReactClass<any>,
};

type State = {
  remeasure: () => void,
  computedStyle?: Object,
  [domProp: string]: any,
};

export default class Responsive extends Component<DefaultProps,Props,State> {
  static defaultProps = {
    component: 'div',
  };

  remeasure: () => void = throttle(() => {
    const {root} = this;
    if (!root) return;
    const {domProps, computedStyleProps} = this.props;
    const nextState: $Shape<State> = {};
    if (domProps) domProps.forEach(prop => nextState[prop] = root[prop]);
    if (computedStyleProps) {
      nextState.computedStyle = {};
      const computedStyle = getComputedStyle(root);
      computedStyleProps.forEach(prop => 
        nextState.computedStyle[prop] = computedStyle[prop]
      );
    }
    this.setState(nextState);
  }, 500);
  // put remeasure in state just so that it gets passed to child 
  // function along with computedStyle and domProps
  state: State = {remeasure: this.remeasure};
  root: ?Object;

  componentDidMount() {
    this.remeasure();
    this.remeasure.flush();
    window.addEventListener('resize', this.remeasure);
  }
  componentWillReceiveProps(nextProps: Props) {
    if (!shallowEqual(this.props.domProps, nextProps.domProps) || 
        !shallowEqual(this.props.computedStyleProps, nextProps.computedStyleProps)) {
      this.remeasure();
    }
  }
  componentWillUnmount() {
    this.remeasure.cancel();
    window.removeEventListener('resize', this.remeasure);
  }
  render(): ?React.Element<any> {
    const {props: {children, component: Comp}, state} = this;
    return <Comp ref={c => this.root = c} children={children(state)}/>;
  }
}

इसके साथ, चौड़ाई में परिवर्तन का जवाब देना बहुत आसान है:

function renderColumns(numColumns: number): React.Element<any> {
  ...
}
const responsiveView = (
  <Responsive domProps={['offsetWidth']}>
    {({offsetWidth}: {offsetWidth: number}): ?React.Element<any> => {
      if (!offsetWidth) return null;
      const numColumns = Math.max(1, Math.floor(offsetWidth / 200));
      return renderColumns(numColumns);
    }}
  </Responsive>
);

इस दृष्टिकोण के बारे में एक प्रश्न मैंने अभी तक जांच नहीं की है कि क्या यह SSR के साथ हस्तक्षेप करता है। मुझे अभी तक यकीन नहीं है कि उस मामले को संभालने का सबसे अच्छा तरीका क्या होगा।
एंडी

महान व्याख्या, इतनी अच्छी तरह से होने के लिए धन्यवाद :) पुनः: SSR, यहाँ एक चर्चा isMounted()है: facebook.github.io/react/blog/2015/12/12/16/…
ptim

1
@memeLab मैंने सिर्फ एक अच्छे रैपर घटक के लिए कोड जोड़ा है जो कि DOM बदलावों के जवाब में अधिकांश बॉयलरप्लेट को बाहर निकालता है, एक नज़र डालें :)
एंडी

1
अगर यह डोम आसान बना देता है तो @Philll_t हाँ यह अच्छा होगा। लेकिन मेरा विश्वास करो, इस पुस्तकालय का उपयोग करने से आपको परेशानी से बचा जाएगा, भले ही यह माप प्राप्त करने का सबसे बुनियादी तरीका न हो।
एंडी

1
@Philll_t एक और चीज़ जो पुस्तकालयों का ध्यान रखती है, वह है ResizeObserver(या एक पॉलीफ़िल) का उपयोग करके अपने कोड में तुरंत आकार अपडेट प्राप्त करना।
एंडी

43

मुझे लगता है कि जिस जीवनचक्र पद्धति की आपको तलाश है, वह है componentDidMount। तत्वों को पहले से ही डोम में रखा गया है और आप घटक के बारे में उनसे जानकारी प्राप्त कर सकते हैंrefs

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

var Container = React.createComponent({

  componentDidMount: function () {
    // if using React < 0.14, use this.refs.svg.getDOMNode().offsetWidth
    var width = this.refs.svg.offsetWidth;
  },

  render: function () {
    <svg ref="svg" />
  }

});

1
सावधान रहें कि offsetWidthवर्तमान में फ़ायरफ़ॉक्स में मौजूद नहीं है।
क्रिस्टोफर चिच जुले

@ChristopherChiche मुझे विश्वास नहीं है कि यह सच है। तुम कौन सा संस्करण चला रहे हो? : यह कम से कम मेरे लिए काम करता है, और MDN दस्तावेजीकरण सुझाव देने के लिए यह माना जा सकता है कि लगता है developer.mozilla.org/en-US/docs/Web/API/CSS_Object_Model/...
couchand

1
खैर मैं हो जाएगा, यह असुविधाजनक है। किसी भी मामले में मेरा उदाहरण शायद एक गरीब था, क्योंकि आपको किसी svgभी तरह स्पष्ट रूप से एक आकार देना चाहिए । AFAIK कुछ भी आप के गतिशील आकार के लिए देख रहे हैं पर शायद भरोसा कर सकते हैं offsetWidth
कौंच

3
0.14 या इसके बाद के संस्करण का उपयोग करने वाले किसी भी व्यक्ति के लिए, .getDOMNode () की अब आवश्यकता नहीं है: facebook.github.io/react/blog/2015/10/07/…
Hamund

2
हालांकि यह विधि तत्व तक पहुंचने के लिए सबसे आसान (और सबसे अधिक jQuery) तरीके की तरह महसूस कर सकती है, फेसबुक अब कहता है "हम इसके खिलाफ सलाह देते हैं क्योंकि स्ट्रिंग रिफ्स में कुछ मुद्दे हैं, विरासत माना जाता है, और भविष्य में से एक में हटाए जाने की संभावना है। रिलीज़। यदि आप वर्तमान में इस का उपयोग कर रहे हैं .refs.textInput refs का उपयोग करने के लिए, हम इसके बजाय कॉलबैक पैटर्न की अनुशंसा करते हैं "। आपको स्ट्रिंग रिफ़ के बजाय कॉलबैक फ़ंक्शन का उपयोग करना चाहिए। जानकारी यहाँ
ahaurat

22

वैकल्पिक रूप से Couchand समाधान के लिए आप findDOMNode का उपयोग कर सकते हैं

var Container = React.createComponent({

  componentDidMount: function () {
    var width = React.findDOMNode(this).offsetWidth;
  },

  render: function () {
    <svg />
  }
});

10
स्पष्ट करने के लिए: React <= 0.12.x में घटक का उपयोग करें ।getDOMNode (), React में = = 0.13.x का उपयोग करें React.findDOMNode ()
pxwise

2
डोम तत्व के लिए अब @pxwise Aaaaaand आपको रिएक्ट 0.14 :) के साथ या तो फ़ंक्शन का उपयोग करने की आवश्यकता नहीं है
एंडी

5
@ मुझे लगता है कि यह ReactDOM.findDOMNode()अब है?
ivarni

1
@MichaelScheper यह सच है, मेरे कोड में कुछ ES7 है। मेरे अद्यतन उत्तर में react-measureप्रदर्शन (मुझे लगता है) शुद्ध ES6 है। यह मुश्किल हो रहा है, निश्चित रूप से शुरू हो रहा है ... मैं पिछले एक साल में एक ही पागलपन के माध्यम से चला गया और एक साढ़े :) :)
एंडी

2
@MichaelScheper btw, आपको यहां कुछ उपयोगी मार्गदर्शन मिल सकता है: github.com/gaearon/react-makes-you-sad
एंडी

6

आप मेरे द्वारा लिखी गई लाइब्रेरी का उपयोग कर सकते हैं जो आपके घटकों को प्रस्तुत किए गए आकार पर नज़र रखता है और आपके माध्यम से इसे पास करता है।

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

import SizeMe from 'react-sizeme';

class MySVG extends Component {
  render() {
    // A size prop is passed into your component by my library.
    const { width, height } = this.props.size;

    return (
     <svg width="100" height="100">
        <circle cx="50" cy="50" r="40" stroke="green" stroke-width="4" fill="yellow" />
     </svg>
    );
  }
} 

// Wrap your component export with my library.
export default SizeMe()(MySVG);   

डेमो: https://react-sizeme-example-esbefmsitg.now.sh/

गिथब: https://github.com/ctrlplusb/react-sizeme

यह एक अनुकूलित स्क्रॉल / ऑब्जेक्ट आधारित एल्गोरिथ्म का उपयोग करता है जिसे मैंने लोगों से उधार लिया है जो मैं जितना अधिक चतुर हूं। :)


अच्छा लगा, शेयर करने के लिए धन्यवाद। मैं 'कस्टम आयामप्रोडाईडिंग' के लिए अपने कस्टम समाधान के लिए एक रेपो बनाने वाला था। लेकिन अब मैं इसे एक कोशिश देने वाला हूं।
लैरीडाहोस्टर

मैं किसी भी प्रतिक्रिया की सराहना करूंगा, सकारात्मक या नकारात्मक :-)
ctrlplusb

इसके लिए शुक्रिया। <Recharts /> आपको एक स्पष्ट चौड़ाई और ऊंचाई निर्धारित करने की आवश्यकता है इसलिए यह बहुत मददगार था
JP DeVries
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.