Jak mogę przekonać Reacta do ponownego renderowania widoku po zmianie rozmiaru okna przeglądarki?
tło
Mam kilka bloków, które chcę układać indywidualnie na stronie, ale chcę też, aby były aktualizowane po zmianie okna przeglądarki. Końcowym rezultatem będzie coś w stylu Bena Holland'a na Pinterest, ale napisane przy użyciu React nie tylko jQuery. Nadal jestem daleko.
Kod
Oto moja aplikacja:
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')
)
Następnie mam Block
komponent (odpowiednik Pin
w powyższym przykładzie 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>
);
}
});
oraz lista / kolekcja 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>
);
}
});
Pytanie
Czy powinienem dodać rozmiar okna jQuery? Jeśli tak to gdzie?
$( window ).resize(function() {
// re-render the component
});
Czy istnieje bardziej „reakcyjny” sposób na zrobienie tego?
źródło
this.updateDimensions
PrzekazaneaddEventListener
jest tylko gołe odniesienia funkcja, która nie będzie miała wartość dlathis
kiedy dzwonił. Czy do dodania należy użyć funkcji anonimowej lub wywołania .bind ()this
, czy też źle zrozumiałem?this
z dowolnymi metodami zdefiniowanymi bezpośrednio na komponencie.innerHeight
iinnerWidth
odwindow
. Możesz pominąć,componentWillMount
jeśli używasz,getInitialState
aby ustawićheight
iwidth
.::
to, że obecnie dostępna jest składnia powiązań sitepoint.com/bind-javascripts-this-keyword-react "operator powiązania (: :) nie będzie częścią ES7, ponieważ zestaw funkcji ES7 został zamrożony w lutym , operator powiązania jest propozycją dla ES8 ”@SophieAlpert ma rację, +1, chcę tylko dostarczyć zmodyfikowaną wersję jej rozwiązania, bez jQuery , w oparciu o tę odpowiedź .
źródło
Bardzo proste rozwiązanie:
źródło
componentWillUnmount()
!forceUpdate
stosuje się warunkowoJest to prosty i krótki przykład użycia es6 bez jQuery.
haki
źródło
::
operator powiązania zwraca nową wartość za każdym razem, gdy jest stosowany. W związku z tym nasłuchiwanie zdarzeń nie zostanie faktycznie wyrejestrowane, ponieważremoveEventListener
kończy się przekazywanie innej funkcji niż pierwotnie przekazanaaddEventListener
.Od React 16.8 możesz używać Haków !
źródło
Edycja 2018 : teraz React ma pierwszorzędne wsparcie kontekstu
Spróbuję udzielić ogólnej odpowiedzi, która dotyczy tego konkretnego problemu, ale także bardziej ogólnego problemu.
Jeśli nie zależy ci na bibliotekach efektów ubocznych, możesz po prostu użyć czegoś takiego jak Packery
Jeśli używasz Fluxa, możesz stworzyć sklep, który zawiera właściwości okna, dzięki czemu zachowasz czystą funkcję renderowania bez konieczności każdorazowego zapytania do obiektu okna.
W innych przypadkach, w których chcesz zbudować responsywną stronę internetową, ale wolisz React style wbudowane zamiast zapytań o media lub chcesz, aby zachowanie HTML / JS zmieniało się w zależności od szerokości okna, czytaj dalej:
Czym jest kontekst React i dlaczego o nim mówię
Kontekst Reakcja an nie znajduje się w publicznym interfejsie API i umożliwia przekazywanie właściwości do całej hierarchii komponentów.
Kontekst React jest szczególnie przydatny do przekazywania do całej aplikacji rzeczy, które nigdy się nie zmieniają (jest używany przez wiele frameworków Flux poprzez mixin). Możesz go używać do przechowywania niezmienników biznesowych aplikacji (takich jak podłączony identyfikator użytkownika, dzięki czemu jest on dostępny wszędzie).
Ale może być również używany do przechowywania rzeczy, które mogą się zmienić. Problem polega na tym, że gdy kontekst się zmienia, wszystkie komponenty, które go używają, powinny zostać zrenderowane i nie jest to łatwe, najlepszym rozwiązaniem jest często odmontowanie / ponowne zamontowanie całej aplikacji w nowym kontekście. Pamiętaj, że forceUpdate nie jest rekurencyjne .
Jak rozumiesz, kontekst jest praktyczny, ale gdy się zmienia, ma wpływ na wydajność, więc raczej nie powinien zmieniać się zbyt często.
Co umieścić w kontekście
Oto rzeczy, które nie zmieniają się często:
Bieżący język użytkownika :
Nie zmienia się bardzo często, a kiedy to robi, ponieważ cała aplikacja jest tłumaczona, musimy ponownie renderować wszystko: bardzo ładny przykład użycia gorącej zmiany języka
Właściwości okna
Szerokość i wysokość nie zmieniają się często, ale kiedy to robimy, nasz układ i zachowanie mogą wymagać dostosowania. Czasem układ można łatwo dostosować za pomocą zapytań multimedialnych CSS, ale czasem nie jest to konieczne i wymaga innej struktury HTML. W przypadku zachowania musisz sobie z tym poradzić za pomocą Javascript.
Nie chcesz ponownie renderować wszystkiego przy każdym zdarzeniu zmiany rozmiaru, więc musisz ogłosić zdarzenia zmiany rozmiaru.
Rozumiem twój problem, że chcesz wiedzieć, ile elementów ma być wyświetlanych zgodnie z szerokością ekranu. Musisz najpierw zdefiniować responsywne punkty przerwania i wyliczyć liczbę różnych typów układów, jakie możesz mieć.
Na przykład:
Podczas zmiany rozmiaru zdarzeń (ogłaszane) można łatwo uzyskać bieżący typ układu, wysyłając zapytanie do obiektu okna.
Następnie możesz porównać typ układu z poprzednim typem układu, a jeśli się zmienił, ponownie renderuj aplikację w nowym kontekście: pozwala to w ogóle uniknąć ponownego renderowania aplikacji, gdy użytkownik wyzwoli zdarzenia zmiany rozmiaru, ale w rzeczywistości typ układu nie zmienił się, więc renderujesz tylko wtedy, gdy jest to wymagane.
Gdy to zrobisz, możesz po prostu użyć typu układu w aplikacji (dostępnego w kontekście), aby dostosować HTML, zachowanie, klasy CSS ... Znasz swój typ układu w funkcji renderowania React, co oznacza, że potrafi bezpiecznie pisać responsywne strony internetowe za pomocą wbudowanych stylów i wcale nie potrzebuje zapytań o media.
Jeśli używasz Fluxa, możesz użyć sklepu zamiast kontekstu React, ale jeśli Twoja aplikacja ma wiele responsywnych komponentów, może łatwiej jest używać kontekstu?
źródło
Korzystam z rozwiązania @senornestor, ale aby być całkowicie poprawnym, musisz również usunąć detektor zdarzeń:
W przeciwnym razie otrzymasz ostrzeżenie:
źródło
Pominąłem wszystkie powyższe odpowiedzi i zacznę używać
react-dimensions
komponentu wyższego rzędu.https://github.com/digidem/react-dimensions
Wystarczy dodać prosty
import
i wywołanie funkcji, można uzyskać dostępthis.props.containerWidth
ithis.props.containerHeight
w składniku.źródło
react-dimensions
nawet działa dla programowo zmienionych wymiarów (np. Zmienił się rozmiar div, co wpływa na rozmiar twojego kontenera, więc musisz zaktualizować swój rozmiar). Odpowiedź Ben Alperta pomaga tylko w przypadku zmiany rozmiaru okna przeglądarki. Zobacz tutaj: github.com/digidem/react-dimensions/issues/4react-dimensions
obsługuje zmiany wielkości okna, zmiany układu Flexbox i zmiany wynikające ze zmiany rozmiaru JavaScript. Myślę, że to obejmuje. Czy masz przykład, że nie obsługuje on poprawnie?window.innerWidth
,window.innerHeight
.react-dimensions
rozwiązuje ważniejszą część problemu, a także uruchamia kod układu przy zmianie rozmiaru okna (a także przy zmianie rozmiaru kontenera).Ten kod używa nowego kontekstowego interfejsu API React :
Następnie możesz użyć go w dowolnym miejscu kodu:
źródło
Nie musisz wymuszać ponownego renderowania.
To może nie pomóc OP, ale w moim przypadku musiałem tylko zaktualizować atrybuty
width
iheight
na moim płótnie (czego nie można zrobić z CSS).To wygląda tak:
źródło
Nie jestem pewien, czy jest to najlepsze podejście, ale to, co zadziałało dla mnie, to pierwsze utworzenie sklepu, nazwałem go WindowStore:
Potem użyłem tego sklepu w moich komponentach, trochę tak:
Do Twojej wiadomości, te pliki zostały częściowo przycięte.
źródło
onresize
może wysłaćWindowResizedAction
.Wiem, że udzielono odpowiedzi, ale pomyślałem, że podzielę się moim rozwiązaniem, ponieważ najlepsza odpowiedź, choć świetna, może być nieco przestarzała.
Jedyną różnicą jest to, że ogłaszam (uruchamiam co 200 ms) updateWindowDimensions na zdarzeniu zmiany rozmiaru, aby nieco zwiększyć wydajność, ALE nie ogłaszam go, gdy jest wywoływany na ComponentDidMount.
Odkryłem, że debaz sprawia, że montowanie jest dość opóźnione, jeśli zdarza się, że często się montuje.
Drobna optymalizacja, ale mam nadzieję, że komuś pomoże!
źródło
initUpdateWindowDimensions
metody?Aby ulepszyć rozwiązanie @ senornestor w użyciu
forceUpdate
i rozwiązanie @ gkri w zakresie usuwaniaresize
detektora zdarzeń z odmontowanego komponentu:bind(this)
w konstruktorzeInną metodą jest użycie stanu „obojętnego” zamiast
forceUpdate
:źródło
Wystarczy zdefiniować funkcję zmiany rozmiaru zdarzenia.
Następnie zaktualizuj rozmiar renderers (płótno), przypisz nowy współczynnik proporcji do kamery.
Odmontowanie i remontowanie to moim zdaniem szalone rozwiązanie ....
poniżej jest mocowanie w razie potrzeby.
źródło
Chciałem udostępnić tę całkiem fajną rzecz, którą właśnie znalazłem
window.matchMedia
.matches
zwróci wartość prawda lub fałsz, jeśli okno jest wyższe lub niższe od określonej wartości maksymalnej szerokości, co oznacza, że nie ma potrzeby ograniczania słuchacza, ponieważ matchMedia uruchamia się tylko raz, gdy zmienia się wartość logiczna.Mój kod można łatwo dostosować w
useState
celu zapisania zwrotów boolean matchMedia i użyć go do warunkowego renderowania komponentu, akcji ognia itp.źródło
Musiałem powiązać go z „tym” w konstruktorze, aby działał ze składnią klasy
źródło
Dziękuję wszystkim za odpowiedzi. Oto mój React + Recompose . Jest to funkcja wyższego rzędu, która zawiera właściwości
windowHeight
iwindowWidth
komponentu.źródło
https://github.com/renatorib/react-sizes jest HOC, aby to zrobić, przy jednoczesnym zachowaniu dobrej wydajności.
źródło
Z tego powodu lepiej jest, jeśli użyjesz tych danych z danych pliku CSS lub JSON, a następnie z tym ustawieniem danych nowy stan za pomocą this.state ({szerokość: „jakaś wartość”, wysokość: „jakaś wartość”}); lub pisanie kodu, który korzysta z danych o szerokości ekranu podczas samodzielnej pracy, jeśli chcesz responsywne obrazy programów
źródło