javascript - How can I respond to the width of an auto-sized DOM element in React? -
i have complex web page using react components, , trying convert page static layout more responsive, resizable layout. however, keep running limitations react, , wondering if there's standard pattern handling these issues. in specific case, have component renders div display:table-cell , width:auto.
unfortunately, cannot query width of component, because can't compute size of element unless it's placed in dom (which has full context deduce actual rendered width). besides using things relative mouse positioning, need set width attributes on svg elements within component.
in addition, when window resizes, how communicate size changes 1 component during setup? we're doing of our 3rd-party svg rendering in shouldcomponentupdate, cannot set state or properties on or other child components within method.
is there standard way of dealing problem using react?
the practical solution use react-measure.
note: code not work react-measure@^2.0.0 api has changed. visit above link see new api.
import measure 'react-measure' const measuredcomp = () => ( <measure> {({width}) => <div>my width {width}</div>} </measure> ) to communicate size changes between components, can pass onmeasure callback , store values receives somewhere (the standard way of sharing state these days use redux):
import measure 'react-measure' import connect 'react-redux' import {setmycompwidth} './actions' // action stores width in somewhere in redux state function select(state) { return { currentwidth: ... // width somewhere in state } } const mycomp = connect(select)(({dispatch, currentwidth}) => ( <measure onmeasure={({width}) => dispatch(setmycompwidth(width))}> <div>mycomp width {currentwidth}</div> </measure> )) how roll own if prefer to:
create wrapper component handles getting values dom , listening window resize events (or component resize detection used react-measure). tell props dom , provide render function taking props child.
what render has mounted before dom props can read; when props aren't available during initial render, might want use style={{visibility: 'hidden'}} user can't see before gets js-computed layout.
// @flow import react, {component} 'react'; import shallowequal 'shallowequal'; import throttle '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 gets passed child // function along computedstyle , 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)}/>; } } with this, responding width changes 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> );
Comments
Post a Comment