La solution la plus pratique consiste à utiliser une bibliothèque pour cela comme react-measure .
Mise à jour : il existe maintenant un hook personnalisé pour la détection de redimensionnement (que je n'ai pas essayé personnellement): react-resize-aware . Étant un crochet personnalisé, il semble plus pratique à utiliser que 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>
)
Pour communiquer les changements de taille entre les composants, vous pouvez passer un onResize
rappel et stocker les valeurs qu'il reçoit quelque part (le moyen standard de partager l'état de nos jours est d'utiliser Redux ):
import * as React from 'react'
import Measure from 'react-measure'
import { useSelector, useDispatch } from 'react-redux'
import { setMyCompWidth } from './actions'
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>
)
}
Comment rouler le vôtre si vous préférez vraiment:
Créez un composant wrapper qui gère l'obtention des valeurs du DOM et l'écoute des événements de redimensionnement de fenêtre (ou la détection de redimensionnement de composant telle qu'utilisée par react-measure
). Vous lui indiquez quels accessoires obtenir du DOM et fournissez une fonction de rendu prenant ces accessoires comme un enfant.
Ce que vous rendez doit être monté avant que les accessoires DOM puissent être lus; lorsque ces accessoires ne sont pas disponibles lors du rendu initial, vous pouvez les utiliser style={{visibility: 'hidden'}}
pour que l'utilisateur ne puisse pas les voir avant d'obtenir une mise en page calculée par JS.
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);
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)}/>;
}
}
Avec cela, répondre aux changements de largeur est très simple:
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>
);
shouldComponentUpdate
c'est le meilleur endroit pour rendre SVG? Cela ressemble à ce que vous voulezcomponentWillReceiveProps
oucomponentWillUpdate
sinonrender
.