Rerender вид в браузере изменить размер с React

313

Как я могу заставить React повторно визуализировать представление при изменении размера окна браузера?

Задний план

У меня есть несколько блоков, которые я хочу разместить на странице по отдельности, но я также хочу, чтобы они обновлялись при изменении окна браузера. Самым конечным результатом будет что-то вроде макета Бена Холланда на Pinterest, но написанное с использованием React, а не только jQuery. Я все еще далеко.

Код

Вот мое приложение:

var MyApp = React.createClass({
  //does the http get from the server
  loadBlocksFromServer: function() {
    $.ajax({
      url: this.props.url,
      dataType: 'json',
      mimeType: 'textPlain',
      success: function(data) {
        this.setState({data: data.events});
      }.bind(this)
    });
  },
  getInitialState: function() {
    return {data: []};
  },
  componentWillMount: function() {
    this.loadBlocksFromServer();

  },    
  render: function() {
    return (
        <div>
      <Blocks data={this.state.data}/>
      </div>
    );
  }
});

React.renderComponent(
  <MyApp url="url_here"/>,
  document.getElementById('view')
)

Тогда у меня есть Blockкомпонент (эквивалентный Pinв приведенном выше примере Pinterest):

var Block = React.createClass({
  render: function() {
    return (
        <div class="dp-block" style={{left: this.props.top, top: this.props.left}}>
        <h2>{this.props.title}</h2>
        <p>{this.props.children}</p>
        </div>
    );
  }
});

и список / коллекция Blocks:

var Blocks = React.createClass({

  render: function() {

    //I've temporarily got code that assigns a random position
    //See inside the function below...

    var blockNodes = this.props.data.map(function (block) {   
      //temporary random position
      var topOffset = Math.random() * $(window).width() + 'px'; 
      var leftOffset = Math.random() * $(window).height() + 'px'; 
      return <Block order={block.id} title={block.summary} left={leftOffset} top={topOffset}>{block.description}</Block>;
    });

    return (
        <div>{blockNodes}</div>
    );
  }
});

Вопрос

Должен ли я добавить изменение размера окна jQuery? Если да, то где?

$( window ).resize(function() {
  // re-render the component
});

Есть ли более «реактивный» способ сделать это?

digibake
источник

Ответы:

531

Использование React Hooks:

Вы можете определить пользовательский Hook, который прослушивает resizeсобытие окна , примерно так:

import React, { useLayoutEffect, useState } from 'react';

function useWindowSize() {
  const [size, setSize] = useState([0, 0]);
  useLayoutEffect(() => {
    function updateSize() {
      setSize([window.innerWidth, window.innerHeight]);
    }
    window.addEventListener('resize', updateSize);
    updateSize();
    return () => window.removeEventListener('resize', updateSize);
  }, []);
  return size;
}

function ShowWindowDimensions(props) {
  const [width, height] = useWindowSize();
  return <span>Window size: {width} x {height}</span>;
}

Преимущество здесь в том, что логика инкапсулирована, и вы можете использовать этот хук в любом месте, где хотите использовать размер окна.

Использование классов React:

Вы можете слушать в componentDidMount, что-то вроде этого компонента, который просто отображает размеры окна (например <span>Window size: 1024 x 768</span>):

import React from 'react';

class ShowWindowDimensions extends React.Component {
  state = { width: 0, height: 0 };
  render() {
    return <span>Window size: {this.state.width} x {this.state.height}</span>;
  }
  updateDimensions = () => {
    this.setState({ width: window.innerWidth, height: window.innerHeight });
  };
  componentDidMount() {
    window.addEventListener('resize', this.updateDimensions);
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.updateDimensions);
  }
}
Софи Альперт
источник
5
Как это работает? this.updateDimensionsПередаются addEventListenerтолько голая ссылка на функцию , которая не будет иметь никакого значения для thisпри вызове. Следует ли использовать анонимную функцию или вызов .bind () для добавления this, или я неправильно понял?
Fadedbee
24
@chrisdew Я немного опоздал, но React автоматически связывает thisлюбые методы, определенные непосредственно в компоненте.
Мишель Тилли
3
@MattDell да, классы ES6 - это просто обычные классы, поэтому с ними не нужно автоматически связываться.
Мишель Тилли
30
Нет необходимости JQuery - использовать innerHeightи innerWidthот window. И вы можете пропустить, componentWillMountесли вы используете getInitialStateдля установки heightи width.
sighrobot
2
@MattDell выглядит как ::синтаксис привязки выходит на текущий sitepoint.com/bind-javascripts-this-keyword-react «оператор связывания (: :) не собирается быть частью ES7, в особенности ES7 набор был заморожен в феврале , оператор связывания - это предложение для ES8 "
Джаррод Смит
130

@SophieAlpert права, +1, я просто хочу предоставить модифицированную версию ее решения без jQuery , основываясь на этом ответе .

var WindowDimensions = React.createClass({
    render: function() {
        return <span>{this.state.width} x {this.state.height}</span>;
    },
    updateDimensions: function() {

    var w = window,
        d = document,
        documentElement = d.documentElement,
        body = d.getElementsByTagName('body')[0],
        width = w.innerWidth || documentElement.clientWidth || body.clientWidth,
        height = w.innerHeight|| documentElement.clientHeight|| body.clientHeight;

        this.setState({width: width, height: height});
        // if you are using ES2015 I'm pretty sure you can do this: this.setState({width, height});
    },
    componentWillMount: function() {
        this.updateDimensions();
    },
    componentDidMount: function() {
        window.addEventListener("resize", this.updateDimensions);
    },
    componentWillUnmount: function() {
        window.removeEventListener("resize", this.updateDimensions);
    }
});
Андре Пена
источник
работает только тогда, когда у вас настроены связанные с IE полизаполнения для прослушивателей vanilla JS
nnnn
@nnnn можешь уточнить? Я признаю, что только проверял это в Chrome. Вы говорите, что window.addEventListener не будет работать в IE без полизаполнений?
Андре Пена
2
@andrerpena caniuse.com/#search=addeventlistener указывает, что ie8 будет иметь проблемы
nnnn
2
@nnnn. Понимаю. Да .. Так что мое решение не работает на IE 8, но работает с 9 на :). Спасибо.
Андре Пена
35
Кто-нибудь действительно все еще заботится о IE8? Или это просто привычка?
морозное чудо
48

Очень простое решение:

resize = () => this.forceUpdate()

componentDidMount() {
  window.addEventListener('resize', this.resize)
}

componentWillUnmount() {
  window.removeEventListener('resize', this.resize)
}
senornestor
источник
12
Не забывайте ограничивать обновление силы, иначе оно будет выглядеть очень странно.
k2snowman69
4
Также не забудьте убрать слушателя componentWillUnmount()!
Джемар Джонс
Это лучшее решение, если оно задушено. Я имею в виду, если forceUpdateэто применяется условно
Asmmahmud
1
Необходимо регулировать не forceUpdate (вызываемую вещь), а запуск события изменения размера, который необходимо регулировать (запуск вещи). События изменения размера могут быть технически вызваны для каждого пикселя при изменении размера окна от большого к маленькому. Когда пользователь делает это быстро, это больше событий, чем вам небезразлично. Хуже того, вы привязываете поток пользовательского интерфейса к потоку Javascript, что означает, что ваше приложение начнет испытывать серьезное медленное чувство, когда оно будет пытаться обрабатывать каждое событие в отдельности.
k2snowman69
1
Вместо того, чтобы запускать функцию изменения размера для каждого пикселя, вы запускаете ее через некоторое короткое периодическое время, чтобы создать иллюзию плавности, с которой вы всегда обрабатываете первое и последнее событие, создавая ощущение, что он плавно обрабатывает изменение размера.
k2snowman69
42

Это простой и короткий пример использования es6 без jQuery.

import React, { Component } from 'react';

export default class CreateContact extends Component {
  state = {
    windowHeight: undefined,
    windowWidth: undefined
  }

  handleResize = () => this.setState({
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth
  });

  componentDidMount() {
    this.handleResize();
    window.addEventListener('resize', this.handleResize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.handleResize)
  }

  render() {
    return (
      <span>
        {this.state.windowWidth} x {this.state.windowHeight}
      </span>
    );
  }
}

крючки

import React, { useEffect, useState } from "react";

let App = () => {
  const [windowWidth, setWindowWidth] = useState(0);
  const [windowHeight, setWindowHeight] = useState(0);
  let resizeWindow = () => {
    setWindowWidth(window.innerWidth);
    setWindowHeight(window.innerHeight);
  };

  useEffect(() => {
    resizeWindow();
    window.addEventListener("resize", resizeWindow);
    return () => window.removeEventListener("resize", resizeWindow);
  }, []);

  return (
    <div>
      <span>
        {windowWidth} x {windowHeight}
      </span>
    </div>
  );
};
голос
источник
4
Это хороший, лаконичный ответ, но у AFAICT есть одна ошибка: если я не ошибаюсь, ::оператор связывания возвращает новое значение каждый раз, когда оно применяется. Таким образом, ваш прослушиватель событий не будет фактически незарегистрированным, так как в removeEventListenerитоге вы передадите функцию, отличную от той, которая была первоначально передана addEventListener.
natevw
21

Начиная с React 16.8 вы можете использовать крючки !

/* globals window */
import React, { useState, useEffect } from 'react'
import _debounce from 'lodash.debounce'

const Example = () => {
  const [width, setWidth] = useState(window.innerWidth)

  useEffect(() => {
    const handleResize = _debounce(() => setWidth(window.innerWidth), 100)

    window.addEventListener('resize', handleResize);

    return () => {
      window.removeEventListener('resize', handleResize);
    }
  }, [])

  return <>Width: {width}</>
}
noetix
источник
13

Edit 2018 : теперь React имеет первоклассную поддержку контекста


Я постараюсь дать общий ответ, который нацелен на эту конкретную проблему, но также и на более общую проблему.

Если вам не нужны побочные эффекты, вы можете просто использовать что-то вроде Packery

Если вы используете Flux, вы можете создать хранилище, содержащее свойства окна, чтобы вы сохраняли чистую функцию рендеринга без необходимости каждый раз запрашивать объект окна.

В других случаях, когда вы хотите создать адаптивный веб-сайт, но предпочитаете встроенные стили React медиазапросам или хотите, чтобы поведение HTML / JS изменялось в зависимости от ширины окна, продолжайте читать:

Что такое контекст React и почему я об этом говорю

Реагирующий контекст an не находится в общедоступном API и позволяет передавать свойства всей иерархии компонентов.

Контекст React особенно полезен для передачи всему вашему приложению того, что никогда не меняется (его используют многие фреймворки Flux через миксин). Вы можете использовать его для хранения бизнес-инвариантов приложения (например, подключенного userId, чтобы он был доступен везде).

Но его также можно использовать для хранения вещей, которые могут измениться. Проблема заключается в том, что при изменении контекста все компоненты, которые его используют, должны быть перерисованы, и это нелегко сделать, лучшее решение часто состоит в том, чтобы размонтировать / перемонтировать все приложение с новым контекстом. Помните, что forceUpdate не является рекурсивным .

Итак, как вы понимаете, контекст практичен, но когда он меняется, он влияет на производительность, поэтому не следует менять его слишком часто.

Что поместить в контекст

  • Инварианты: как подключенный userId, sessionToken, что угодно ...
  • Вещи, которые не часто меняются

Вот вещи, которые не часто меняются:

Текущий язык пользователя :

Это не очень часто меняется, и когда это происходит, когда переводится все приложение, мы должны перерисовывать все: очень хороший пример использования горячих изменений языка.

Свойства окна

Ширина и высота меняются не часто, но когда мы делаем наш макет и поведение, возможно, придется адаптироваться. Для макета иногда легко настроить с помощью медиазапросов CSS, но иногда это не так и требует другой структуры HTML. Для поведения вы должны справиться с этим с помощью Javascript.

Вы не хотите перерисовывать все при каждом событии изменения размера, поэтому вам нужно отменить изменения размера события.

Что я понимаю в вашей проблеме, так это то, что вы хотите знать, сколько элементов отображается в зависимости от ширины экрана. Итак, вы должны сначала определить отзывчивые точки останова и перечислить количество различных типов макетов, которые вы можете иметь.

Например:

  • Макет "1col", для ширины <= 600
  • Макет "2col", для 600 <ширина <1000
  • Макет "3col", для 1000 <= ширина

По событиям изменения размера (опровергнутым) вы можете легко получить текущий тип макета, запросив объект окна.

Затем вы можете сравнить тип макета с прежним типом макета и, если он изменился, повторно выполнить рендеринг приложения с новым контекстом: это позволяет избежать повторного рендеринга приложения вообще, когда пользователь имеет триггерные события изменения размера, но на самом деле Тип макета не изменился, поэтому вы можете выполнить повторную визуализацию только при необходимости.

Как только вы это сделаете, вы можете просто использовать тип макета внутри вашего приложения (доступный через контекст), чтобы вы могли настроить HTML, поведение, классы CSS ... Вы знаете свой тип макета внутри функции рендеринга React, так что это означает, что вы может безопасно писать адаптивные веб-сайты, используя встроенные стили, и вообще не нуждается в медиазапросах.

Если вы используете Flux, вы можете использовать хранилище вместо контекста React, но если в вашем приложении много отзывчивых компонентов, может быть, проще использовать контекст?

Себастьян Лорбер
источник
10

Я использую решение @senornestor, но, чтобы быть полностью правильным, вы также должны удалить прослушиватель событий:

componentDidMount() {
    window.addEventListener('resize', this.handleResize);
}

componentWillUnmount(){
    window.removeEventListener('resize', this.handleResize);
}

handleResize = () => {
    this.forceUpdate();
};

В противном случае вы получите предупреждение:

Предупреждение: forceUpdate (...): может обновлять только смонтированный или монтируемый компонент. Обычно это означает, что вы вызвали forceUpdate () для неустановленного компонента. Это неоперация. Пожалуйста, проверьте код для компонента XXX.

gkri
источник
1
Вы получаете предупреждение по причине: то, что вы делаете, это плохая практика. Ваша функция render () должна быть чистой функцией реквизита и состояния. В вашем случае вы должны сохранить новый размер в состоянии.
zoran404
7

Я бы пропустил все приведенные выше ответы и начал бы использовать react-dimensionsкомпонент высшего порядка.

https://github.com/digidem/react-dimensions

Просто добавьте простой importи функциональный вызов, и вы сможете получить доступ this.props.containerWidthи this.props.containerHeightк своему компоненту.

// Example using ES6 syntax
import React from 'react'
import Dimensions from 'react-dimensions'

class MyComponent extends React.Component {
  render() (
    <div
      containerWidth={this.props.containerWidth}
      containerHeight={this.props.containerHeight}
    >
    </div>
  )
}

export default Dimensions()(MyComponent) // Enhanced component
MindJuice
источник
1
Это не дает размер окна, просто контейнер.
Майтамлин
3
Да, вот и весь смысл. Размер окна тривиально найти. Размер контейнера намного сложнее найти и гораздо полезнее для компонента React. Последняя версия react-dimensionsдаже работает для программно измененных размеров (например, изменился размер элемента div, который влияет на размер вашего контейнера, поэтому вам необходимо обновить свой размер). Ответ Бена Альперта помогает только при изменении размера окна браузера. Смотрите здесь: github.com/digidem/react-dimensions/issues/4
MindJuice
1
react-dimensionsобрабатывает изменения размера окна, изменения макета flexbox и изменения из-за изменения размера JavaScript. Я думаю, что это охватывает. У вас есть пример, что он не справляется должным образом?
MindJuice
2
Размер окна тривиально получить. Нет необходимости в библиотеке , что: window.innerWidth, window.innerHeight. react-dimensionsрешает более важную часть проблемы, а также запускает код макета при изменении размера окна (а также при изменении размера контейнера).
MindJuice
1
Этот проект больше не поддерживается.
Parabolord
7

Этот код использует новый контекстный API React :

  import React, { PureComponent, createContext } from 'react';

  const { Provider, Consumer } = createContext({ width: 0, height: 0 });

  class WindowProvider extends PureComponent {
    state = this.getDimensions();

    componentDidMount() {
      window.addEventListener('resize', this.updateDimensions);
    }

    componentWillUnmount() {
      window.removeEventListener('resize', this.updateDimensions);
    }

    getDimensions() {
      const w = window;
      const d = document;
      const documentElement = d.documentElement;
      const body = d.getElementsByTagName('body')[0];
      const width = w.innerWidth || documentElement.clientWidth || body.clientWidth;
      const height = w.innerHeight || documentElement.clientHeight || body.clientHeight;

      return { width, height };
    }

    updateDimensions = () => {
      this.setState(this.getDimensions());
    };

    render() {
      return <Provider value={this.state}>{this.props.children}</Provider>;
    }
  }

Затем вы можете использовать его где угодно в своем коде, например так:

<WindowConsumer>
  {({ width, height }) =>  //do what you want}
</WindowConsumer>
Альберт Оливе
источник
6

Вам не обязательно форсировать повторную визуализацию.

Это не может помочь О.П., но в моем случае я только нужно обновить widthи heightатрибуты на моем холсте (которые вы не можете сделать с помощью CSS).

Это выглядит так:

import React from 'react';
import styled from 'styled-components';
import {throttle} from 'lodash';

class Canvas extends React.Component {

    componentDidMount() {
        window.addEventListener('resize', this.resize);
        this.resize();
    }

    componentWillUnmount() {
        window.removeEventListener('resize', this.resize);
    }

    resize = throttle(() => {
        this.canvas.width = this.canvas.parentNode.clientWidth;
        this.canvas.height = this.canvas.parentNode.clientHeight;
    },50)

    setRef = node => {
        this.canvas = node;
    }

    render() {
        return <canvas className={this.props.className} ref={this.setRef} />;
    }
}

export default styled(Canvas)`
   cursor: crosshair;
`
mpen
источник
5

Не уверен, что это лучший подход, но для меня сначала было создать Магазин, я назвал его WindowStore:

import {assign, events} from '../../libs';
import Dispatcher from '../dispatcher';
import Constants from '../constants';

let CHANGE_EVENT = 'change';
let defaults = () => {
    return {
        name: 'window',
        width: undefined,
        height: undefined,
        bps: {
            1: 400,
            2: 600,
            3: 800,
            4: 1000,
            5: 1200,
            6: 1400
        }
    };
};
let save = function(object, key, value) {
    // Save within storage
    if(object) {
        object[key] = value;
    }

    // Persist to local storage
    sessionStorage[storage.name] = JSON.stringify(storage);
};
let storage;

let Store = assign({}, events.EventEmitter.prototype, {
    addChangeListener: function(callback) {
        this.on(CHANGE_EVENT, callback);
        window.addEventListener('resize', () => {
            this.updateDimensions();
            this.emitChange();
        });
    },
    emitChange: function() {
        this.emit(CHANGE_EVENT);
    },
    get: function(keys) {
        let value = storage;

        for(let key in keys) {
            value = value[keys[key]];
        }

        return value;
    },
    initialize: function() {
        // Set defaults
        storage = defaults();
        save();
        this.updateDimensions();
    },
    removeChangeListener: function(callback) {
        this.removeListener(CHANGE_EVENT, callback);
        window.removeEventListener('resize', () => {
            this.updateDimensions();
            this.emitChange();
        });
    },
    updateDimensions: function() {
        storage.width =
            window.innerWidth ||
            document.documentElement.clientWidth ||
            document.body.clientWidth;
        storage.height =
            window.innerHeight ||
            document.documentElement.clientHeight ||
            document.body.clientHeight;
        save();
    }
});

export default Store;

Затем я использовал этот магазин в своих компонентах, вроде этого:

import WindowStore from '../stores/window';

let getState = () => {
    return {
        windowWidth: WindowStore.get(['width']),
        windowBps: WindowStore.get(['bps'])
    };
};

export default React.createClass(assign({}, base, {
    getInitialState: function() {
        WindowStore.initialize();

        return getState();
    },
    componentDidMount: function() {
        WindowStore.addChangeListener(this._onChange);
    },
    componentWillUnmount: function() {
        WindowStore.removeChangeListener(this._onChange);
    },
    render: function() {
        if(this.state.windowWidth < this.state.windowBps[2] - 1) {
            // do something
        }

        // return
        return something;
    },
    _onChange: function() {
        this.setState(getState());
    }
}));

К вашему сведению, эти файлы были частично обрезаны.

Дэвид Синклер
источник
1
Состояние хранилища должно изменяться только в ответ на отправленное действие. Это определенно не хороший способ сделать это.
морозный
2
@frostymarvelous действительно, возможно, лучше переместить в компонент, как в других ответах.
Дэвид Синклер
@DavidSinclair Или даже лучше, onresizeможет отправить WindowResizedAction.
Джон Вайс
1
Это излишне.
Джейсон Райс
5

Я знаю, что на этот вопрос уже был дан ответ, но подумал, что поделюсь своим решением, так как лучший ответ, хотя и отличный, может теперь немного устареть.

    constructor (props) {
      super(props)

      this.state = { width: '0', height: '0' }

      this.initUpdateWindowDimensions = this.updateWindowDimensions.bind(this)
      this.updateWindowDimensions = debounce(this.updateWindowDimensions.bind(this), 200)
    }

    componentDidMount () {
      this.initUpdateWindowDimensions()
      window.addEventListener('resize', this.updateWindowDimensions)
    }

    componentWillUnmount () {
      window.removeEventListener('resize', this.updateWindowDimensions)
    }

    updateWindowDimensions () {
      this.setState({ width: window.innerWidth, height: window.innerHeight })
    }

Единственное отличие на самом деле в том, что я отменяю (только каждые 200 мс) updateWindowDimensions для события resize, чтобы немного повысить производительность, НО не отменяю его при вызове в ComponentDidMount.

Я обнаружил, что отскок сделал его довольно запоздалым, если он часто монтируется.

Просто небольшая оптимизация, но надеюсь, она кому-нибудь поможет!

Мэтт Уиллс
источник
Где определение initUpdateWindowDimensionsметода?
Мэтт
Это определено в конструкторе :) это просто updateWindowDimensions, привязанный к компоненту, но без отладки.
Мэтт Уиллс
3

Просто чтобы улучшить решение @ senornestor для использования forceUpdateи решение @ gkri для удаления resizeпрослушивателя событий на компоненте unmount:

  1. не забудьте ограничить (или отменить) вызов для изменения размера
  2. убедитесь, что bind(this)в конструкторе
import React from 'react'
import { throttle } from 'lodash'

class Foo extends React.Component {
  constructor(props) {
    super(props)
    this.resize = throttle(this.resize.bind(this), 100)
  }

  resize = () => this.forceUpdate()

  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }

  render() {
    return (
      <div>{window.innerWidth} x {window.innerHeight}</div>
    )
  }
}

Другой метод - просто использовать "фиктивное" состояние вместо forceUpdate:

import React from 'react'
import { throttle } from 'lodash'

class Foo extends React.Component {
  constructor(props) {
    super(props)
    this.state = { foo: 1 }
    this.resize = throttle(this.resize.bind(this), 100)
  }

  resize = () => this.setState({ foo: 1 })

  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }

  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }

  render() {
    return (
      <div>{window.innerWidth} x {window.innerHeight}</div>
    )
  }
}
kimbaudi
источник
2
componentDidMount() {

    // Handle resize
    window.addEventListener('resize', this.handleResize);
}




handleResize = () => {
    this.renderer.setSize(this.mount.clientWidth, this.mount.clientHeight);
    this.camera.aspect = this.mount.clientWidth / this.mount.clientHeight;
    this.camera.updateProjectionMatrix();
};

Нужно только определить функцию изменения размера события.

Затем обновите размер рендера (холст), назначьте новое соотношение сторон для камеры.

Размонтирование и перенастройка - сумасшедшее решение, на мой взгляд ....

ниже крепление при необходимости.

            <div
                className={this.state.canvasActive ? 'canvasContainer isActive' : 'canvasContainer'}
                ref={mount => {
                    this.mount = mount;
                }}
            />
Николай Хеккенс
источник
2

Хотел поделиться этой довольно классной вещью, которую я только что нашел, используя window.matchMedia

const mq = window.matchMedia('(max-width: 768px)');

  useEffect(() => {
    // initial check to toggle something on or off
    toggle();

    // returns true when window is <= 768px
    mq.addListener(toggle);

    // unmount cleanup handler
    return () => mq.removeListener(toggle);
  }, []);

  // toggle something based on matchMedia event
  const toggle = () => {
    if (mq.matches) {
      // do something here
    } else {
      // do something here
    }
  };

.matches вернет true или false, если окно больше или меньше указанного значения max-width, это означает, что нет необходимости регулировать слушателя, так как matchMedia запускается только один раз, когда изменяется логическое значение.

Мой код можно легко настроить, чтобы включить useStateдля сохранения логических возвращений matchMedia и использовать его для условной визуализации компонента, действия при пожаре и т. Д.

Райли Браун
источник
1

Пришлось связать его с этим в конструкторе, чтобы он работал с синтаксисом класса

class MyComponent extends React.Component {
  constructor(props) {
    super(props)
    this.resize = this.resize.bind(this)      
  }
  componentDidMount() {
    window.addEventListener('resize', this.resize)
  }
  componentWillUnmount() {
    window.removeEventListener('resize', this.resize)
  }
}
Джим перрис
источник
1

Спасибо всем за ответы. Вот мой Реакт + Рекомендовать . Это функция высокого порядка, которая включает в себя windowHeightи windowWidthсвойства компонента.

const withDimensions = compose(
 withStateHandlers(
 ({
   windowHeight,
   windowWidth
 }) => ({
   windowHeight: window.innerHeight,
   windowWidth: window.innerWidth
 }), {
  handleResize: () => () => ({
    windowHeight: window.innerHeight,
    windowWidth: window.innerWidth
  })
 }),
 lifecycle({
   componentDidMount() {
   window.addEventListener('resize', this.props.handleResize);
 },
 componentWillUnmount() {
  window.removeEventListener('resize');
 }})
)
dumorango
источник
1

https://github.com/renatorib/react-sizes - это HOC, который делает это, сохраняя при этом хорошую производительность.

import React from 'react'
import withSizes from 'react-sizes'

@withSizes(({ width }) => ({ isMobile: width < 480 }))
class MyComponent extends Component {
  render() {
    return <div>{this.props.isMobile ? 'Is Mobile' : 'Is Not Mobile'}</div>
  }
}

export default MyComponent
Рассел Коэн
источник
0

По этой причине лучше, если вы используете эти данные из данных файла CSS или JSON, а затем с этими данными устанавливаете новое состояние с помощью this.state ({width: "some value", height: "some value"}); или написание кода, который использует данные ширины экрана в самостоятельной работе, если вы хотите, чтобы изображения показывались быстро

Миодраг Траянович
источник