Как я могу ответить на ширину элемента DOM с автоматическим размером в React?
У меня есть сложная веб-страница с использованием компонентов React, и я пытаюсь преобразовать страницу из статического макета в более отзывчивый, изменяемый размер макета. Тем не менее, я продолжаю сталкиваться с ограничениями с React, и мне интересно, есть ли стандартный шаблон для обработки этих проблем. В моем конкретном случае у меня есть компонент, который отображается как div с display:table-cell и width:auto.
к сожалению, я не могу запросить ширина моего компонента, потому что вы не можете вычислить размер элемент, если он фактически не помещен в DOM (который имеет полный контекст, с помощью которого можно вывести фактическую ширину визуализации). Помимо использования этого для таких вещей, как относительное позиционирование мыши, мне также нужно это, чтобы правильно установить атрибуты ширины на элементах SVG в компоненте.
кроме того, когда размер окна изменяется, как я могу передавать изменения размера от одного компонента к другому во время установки? Мы делаем все наши сторонние SVG рендеринга в shouldComponentUpdate, но вы не можете установите состояние или свойства для себя или других дочерних компонентов в этом методе.
есть ли стандартный способ решения этой проблемы с помощью React?
4 ответов:
наиболее практичным решением является использование react-measure.
Примечание: этот код не работает с
react-measure@^2.0.0как API изменился. Перейдите по ссылке выше, чтобы увидеть новый API.import Measure from 'react-measure' const MeasuredComp = () => ( <Measure> {({width}) => <div>My width is {width}</div>} </Measure> )чтобы передать изменения размера между компонентами, вы можете передать
onMeasureобратный вызов и хранить значения, которые он получает где-то (стандартный способ совместного использования состояния в эти дни является использование возвращение):import Measure from 'react-measure' import connect from 'react-redux' import {setMyCompWidth} from './actions' // some action that stores width in somewhere in redux state function select(state) { return { currentWidth: ... // get width from somewhere in the state } } const MyComp = connect(select)(({dispatch, currentWidth}) => ( <Measure onMeasure={({width}) => dispatch(setMyCompWidth(width))}> <div>MyComp width is {currentWidth}</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> );
Я думаю, что метод жизненного цикла, который вы ищете, это
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" /> } });
в качестве альтернативы решению couchand вы можете использовать findDOMNode
var Container = React.createComponent({ componentDidMount: function () { var width = React.findDOMNode(this).offsetWidth; }, render: function () { <svg /> } });
вы можете использовать библиотеку 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);
Demo:https://react-sizeme-example-esbefmsitg.now.sh/
Github:https://github.com/ctrlplusb/react-sizeme
Он использует оптимизированный алгоритм прокрутки / объекта, который я заимствовал у людей гораздо более умных, чем я. :)
Comments