Saya memiliki halaman web yang kompleks menggunakan komponen React, dan saya mencoba untuk mengubah halaman dari tata letak statis menjadi tata letak yang lebih responsif dan dapat diubah ukurannya. Namun, saya terus mengalami keterbatasan dengan React, dan bertanya-tanya apakah ada pola standar untuk menangani masalah ini. Dalam kasus khusus saya, saya memiliki komponen yang dirender sebagai div dengan display: table-cell dan width: auto.
Sayangnya, saya tidak dapat menanyakan lebar komponen saya, karena Anda tidak dapat menghitung ukuran elemen kecuali itu benar-benar ditempatkan di DOM (yang memiliki konteks lengkap untuk menyimpulkan lebar yang diberikan sebenarnya). Selain menggunakan ini untuk hal-hal seperti posisi mouse relatif, saya juga membutuhkan ini untuk mengatur atribut lebar dengan benar pada elemen SVG di dalam komponen.
Selain itu, saat jendela berubah ukuran, bagaimana cara mengkomunikasikan perubahan ukuran dari satu komponen ke komponen lainnya selama penyetelan? Kami melakukan semua rendering SVG pihak ketiga kami di shouldComponentUpdate, tetapi Anda tidak dapat menyetel status atau properti pada diri Anda sendiri atau komponen turunan lainnya dalam metode itu.
Apakah ada cara standar untuk mengatasi masalah ini menggunakan React?
sumber
shouldComponentUpdate
tempat terbaik untuk merender SVG? Kedengarannya seperti apa yang Anda inginkancomponentWillReceiveProps
ataucomponentWillUpdate
jika tidakrender
.Jawaban:
Solusi paling praktis adalah menggunakan perpustakaan untuk pengukuran reaksi seperti ini .
Pembaruan : sekarang ada kait khusus untuk deteksi pengubahan ukuran (yang belum saya coba secara pribadi): react-resize-aware . Menjadi pengait khusus, terlihat lebih nyaman digunakan daripada
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> )
Untuk mengomunikasikan perubahan ukuran antar komponen, Anda dapat meneruskan
onResize
callback dan menyimpan nilai yang diterimanya di suatu tempat (cara standar berbagi status saat ini adalah dengan menggunakan 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> ) }
Cara menggulung sendiri jika Anda lebih suka:
Buat komponen pembungkus yang menangani pengambilan nilai dari DOM dan mendengarkan peristiwa pengubahan ukuran jendela (atau deteksi pengubahan ukuran komponen seperti yang digunakan oleh
react-measure
). Anda memberi tahu alat peraga mana yang akan didapat dari DOM dan menyediakan fungsi render yang mengambil alat peraga tersebut sebagai anak.Apa yang Anda render harus sudah terpasang sebelum alat peraga DOM dapat dibaca; ketika props tersebut tidak tersedia selama rendering awal, Anda mungkin ingin menggunakan
style={{visibility: 'hidden'}}
sehingga pengguna tidak dapat melihatnya sebelum mendapatkan tata letak yang dihitung 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)}/>; } }
Dengan ini, menanggapi perubahan lebar sangat sederhana:
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> );
sumber
isMounted()
sini: facebook.github.io/react/blog/2015/12/16/…ResizeObserver
(atau polyfill) untuk mendapatkan pembaruan ukuran pada kode Anda dengan segera.Saya pikir metode siklus hidup yang Anda cari adalah
componentDidMount
. Elemen telah ditempatkan di DOM dan Anda bisa mendapatkan informasi tentang mereka dari komponenrefs
.Misalnya:
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" /> } });
sumber
offsetWidth
saat ini tidak ada di Firefox.svg
elemen. AFAIK untuk apa pun yang Anda cari untuk ukuran dinamis yang mungkin dapat Anda andalkanoffsetWidth
.Alternatifnya untuk solusi sofa Anda dapat menggunakan findDOMNode
var Container = React.createComponent({ componentDidMount: function () { var width = React.findDOMNode(this).offsetWidth; }, render: function () { <svg /> } });
sumber
ReactDOM.findDOMNode()
sekarang?react-measure
demonstrasinya adalah (menurut saya) ES6 murni. Sulit untuk memulainya, pasti ... Saya mengalami kegilaan yang sama selama satu setengah tahun terakhir :)Anda dapat menggunakan I library yang saya tulis yang memonitor ukuran komponen yang Anda berikan dan meneruskannya kepada Anda.
Sebagai contoh:
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
Ini menggunakan algoritma berbasis gulir / objek yang dioptimalkan yang saya pinjam dari orang-orang yang jauh lebih pintar daripada saya. :)
sumber