У меня сложная веб-страница с использованием компонентов React, и я пытаюсь преобразовать страницу из статического макета в более отзывчивый макет с изменяемым размером. Однако я продолжаю сталкиваться с ограничениями в React, и мне интересно, есть ли стандартный шаблон для решения этих проблем. В моем конкретном случае у меня есть компонент, который отображается как div с display: table-cell и width: auto.
К сожалению, я не могу запросить ширину своего компонента, потому что вы не можете вычислить размер элемента, если он фактически не помещен в DOM (который имеет полный контекст, с помощью которого можно определить фактическую ширину визуализации). Помимо использования этого для таких вещей, как относительное позиционирование мыши, мне также нужно это для правильной установки атрибутов ширины для элементов SVG в компоненте.
Кроме того, при изменении размера окна, как мне сообщить об изменении размера от одного компонента к другому во время установки? Мы выполняем всю нашу стороннюю визуализацию SVG в shouldComponentUpdate, но вы не можете устанавливать состояние или свойства для себя или других дочерних компонентов в этом методе.
Есть ли стандартный способ решения этой проблемы с помощью React?
источник
shouldComponentUpdate
это лучшее место для рендеринга SVG? Похоже, вы хотите,componentWillReceiveProps
аcomponentWillUpdate
если нетrender
.Ответы:
Наиболее практичным решением является использование библиотеки, например, реакции-меры .
Обновление : теперь есть настраиваемый хук для обнаружения изменения размера (который я лично не пробовал): response-resize-aware . Будучи кастомным хуком, он выглядит более удобным в использовании, чем
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> ) }
Как кататься самостоятельно, если вы действительно предпочитаете:
Создайте компонент-оболочку, который обрабатывает получение значений из модели DOM и прослушивание событий изменения размера окна (или обнаружение изменения размера компонента, которое используется
react-measure
). Вы указываете ему, какие свойства нужно получить из DOM, и предоставляете функцию рендеринга, которая принимает эти свойства как дочерние.То, что вы визуализируете, необходимо смонтировать, прежде чем можно будет прочитать свойства 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> );
источник
isMounted()
: facebook.github.io/react/blog/2015/12/16/…ResizeObserver
(или полифил) для немедленного получения обновлений размера вашего кода.Я думаю, что вам нужен метод жизненного цикла
componentDidMount
. Элементы уже размещены в DOM, и вы можете получить информацию о них из компонента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" /> } });
источник
offsetWidth
этого сейчас нет в Firefox.svg
все равно должны явно указать размер элемента. AFAIK для всего, что вы ищете, на динамический размер вы, вероятно, можете положитьсяoffsetWidth
.В качестве альтернативы решению couchand вы можете использовать findDOMNode
var Container = React.createComponent({ componentDidMount: function () { var width = React.findDOMNode(this).offsetWidth; }, render: function () { <svg /> } });
источник
ReactDOM.findDOMNode()
сейчас?react-measure
демонстрация (я думаю) чистый ES6. Трудно начинать, конечно ... Я прошел через то же безумие за последние полтора года :)Вы можете использовать библиотеку I, которую я написал, которая отслеживает размер ваших компонентов и передает их вам.
Например:
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/
Github: https://github.com/ctrlplusb/react-sizeme
Он использует оптимизированный алгоритм на основе прокрутки / объектов, который я позаимствовал у людей, гораздо более умных, чем я. :)
источник