Komunikacja między natywnym a React Native
Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
W przewodniku Integracja z istniejącymi aplikacjami oraz przewodniku po natywnych komponentach UI uczymy się, jak osadzać React Native w natywnych komponentach i odwrotnie. Gdy mieszamy natywne komponenty z React Native, w końcu pojawi się potrzeba komunikacji między tymi dwoma światami. Niektóre sposoby osiągnięcia tego zostały już wspomniane w innych przewodnikach. Ten artykuł podsumowuje dostępne techniki.
Wprowadzenie
React Native jest inspirowany Reactem, więc podstawowa koncepcja przepływu informacji jest podobna. Przepływ w React jest jednokierunkowy. Utrzymujemy hierarchię komponentów, w której każdy komponent zależy wyłącznie od swojego rodzica i własnego stanu wewnętrznego. Osiągamy to za pomocą właściwości: dane są przekazywane od rodzica do dzieci w sposób odgórny. Jeśli komponent przodka zależy od stanu potomka, należy przekazać funkcję zwrotną (callback), którą potomek użyje do aktualizacji przodka.
Ta sama koncepcja dotyczy React Native. Dopóki budujemy aplikację wyłącznie w ramach frameworka, możemy sterować aplikacją za pomocą właściwości i funkcji zwrotnych. Jednak gdy mieszamy komponenty React Native z natywnymi, potrzebujemy specyficznych, międzyjęzykowych mechanizmów, które umożliwią przekazywanie informacji między nimi.
Zatrzymuje działającą animację i resetuje wartość do oryginalnej.
Właściwości to najbardziej bezpośredni sposób komunikacji międzykomponentowej. Potrzebujemy więc sposobu przekazywania właściwości zarówno z natywnego do React Native, jak i z React Native do natywnego.
Przekazywanie właściwości z natywnego do React Native
Możesz przekazać właściwości do aplikacji React Native, dostarczając niestandardową implementację ReactActivityDelegate w głównej aktywności. Ta implementacja powinna nadpisać getLaunchOptions, aby zwrócić Bundle z żądanymi właściwościami.
- Java
- Kotlin
public class MainActivity extends ReactActivity {
@Override
protected ReactActivityDelegate createReactActivityDelegate() {
return new ReactActivityDelegate(this, getMainComponentName()) {
@Override
protected Bundle getLaunchOptions() {
Bundle initialProperties = new Bundle();
ArrayList<String> imageList = new ArrayList<String>(Arrays.asList(
"https://dummyimage.com/600x400/ffffff/000000.png",
"https://dummyimage.com/600x400/000000/ffffff.png"
));
initialProperties.putStringArrayList("images", imageList);
return initialProperties;
}
};
}
}
class MainActivity : ReactActivity() {
override fun createReactActivityDelegate(): ReactActivityDelegate {
return object : ReactActivityDelegate(this, mainComponentName) {
override fun getLaunchOptions(): Bundle {
val imageList = arrayListOf("https://dummyimage.com/600x400/ffffff/000000.png", "https://dummyimage.com/600x400/000000/ffffff.png")
val initialProperties = Bundle().apply { putStringArrayList("images", imageList) }
return initialProperties
}
}
}
}
import React from 'react';
import {View, Image} from 'react-native';
export default class ImageBrowserApp extends React.Component {
renderImage(imgURI) {
return <Image source={{uri: imgURI}} />;
}
render() {
return <View>{this.props.images.map(this.renderImage)}</View>;
}
}
ReactRootView udostępnia właściwość do odczytu i zapisu appProperties. Po ustawieniu appProperties, aplikacja React Native jest ponownie renderowana z nowymi właściwościami. Aktualizacja jest wykonywana tylko wtedy, gdy nowe właściwości różnią się od poprzednich.
- Java
- Kotlin
Bundle updatedProps = mReactRootView.getAppProperties();
ArrayList<String> imageList = new ArrayList<String>(Arrays.asList(
"https://dummyimage.com/600x400/ff0000/000000.png",
"https://dummyimage.com/600x400/ffffff/ff0000.png"
));
updatedProps.putStringArrayList("images", imageList);
mReactRootView.setAppProperties(updatedProps);
var updatedProps: Bundle = reactRootView.getAppProperties()
var imageList = arrayListOf("https://dummyimage.com/600x400/ff0000/000000.png", "https://dummyimage.com/600x400/ffffff/ff0000.png")
Możesz aktualizować właściwości w dowolnym momencie. Jednak aktualizacje muszą być wykonywane w wątku głównym. Odczyt możesz wykonywać w dowolnym wątku.
Nie ma możliwości aktualizacji tylko części właściwości. Sugerujemy zaimplementowanie własnego opakowania.
Obecnie funkcja JS componentWillUpdateProps najwyższego poziomu komponentu RN nie zostanie wywołana po aktualizacji właściwości. Możesz jednak uzyskać dostęp do nowych właściwości w funkcji componentDidMount.
Przekazywanie właściwości z React Native do natywnego
Problem udostępniania właściwości natywnych komponentów jest szczegółowo omówiony w tym artykule. W skrócie, właściwości, które mają być widoczne w JavaScriptie, muszą być udostępnione jako metody setterów oznaczane adnotacją @ReactProp, a następnie używane w React Native tak, jakby komponent był zwykłym komponentem React Native.
Ograniczenia właściwości
Główną wadą właściwości międzyjęzykowych jest brak wsparcia dla funkcji zwrotnych, które umożliwiłyby obsługę wiązań danych w kierunku od dołu do góry. Wyobraź sobie, że masz mały widok RN, który chcesz usunąć z natywnego widoku nadrzędnego w wyniku akcji JS. Nie da się tego osiągnąć za pomocą właściwości, ponieważ informacja musiałaby płynąć od dołu do góry.
Mimo że mamy rodzaj międzyjęzykowych funkcji zwrotnych (opisanych tutaj), nie zawsze są one tym, czego potrzebujemy. Główny problem polega na tym, że nie są one przeznaczone do przekazywania jako właściwości. Ten mechanizm pozwala raczej wywołać natywną akcję z JS i obsłużyć jej wynik w JS.
Inne sposoby interakcji międzyjęzykowej (zdarzenia i moduły natywne)
Jak wspomniano w poprzednim rozdziale, używanie właściwości ma pewne ograniczenia. Czasami właściwości nie wystarczą do sterowania logiką naszej aplikacji i potrzebujemy rozwiązania, które zapewni większą elastyczność. Ten rozdział omawia inne techniki komunikacji dostępne w React Native. Można je wykorzystać zarówno do komunikacji wewnętrznej (pomiędzy warstwami JS i natywnymi w RN), jak i zewnętrznej (pomiędzy RN a „czysto natywną” częścią aplikacji).
React Native umożliwia wykonywanie wywołań funkcji międzyjęzykowych. Możesz wykonywać niestandardowy kod natywny z poziomu JS i odwrotnie. Niestety, w zależności od strony, na której pracujemy, ten sam cel osiągamy w różny sposób. Dla kodu natywnego używamy mechanizmu zdarzeń do zaplanowania wykonania funkcji obsługi w JS, podczas gdy dla React Native bezpośrednio wywołujemy metody eksportowane przez moduły natywne.
Wywoływanie funkcji React Native z kodu natywnego (zdarzenia)
Zdarzenia są szczegółowo opisane w tym artykule. Należy pamiętać, że użycie zdarzeń nie daje gwarancji co do czasu wykonania, ponieważ zdarzenie jest obsługiwane w osobnym wątku.
Zdarzenia są potężne, ponieważ pozwalają nam zmieniać komponenty React Native bez konieczności posiadania do nich referencji. Istnieją jednak pewne pułapki, w które można wpaść podczas ich używania:
-
Ponieważ zdarzenia mogą być wysyłane z dowolnego miejsca, mogą wprowadzać do projektu zależności w stylu spaghetti.
-
Zdarzenia współdzielą przestrzeń nazw, co oznacza, że możesz napotkać kolizje nazw. Kolizje nie są wykrywane statycznie, co utrudnia ich debugowanie.
-
Jeśli używasz kilku instancji tego samego komponentu React Native i chcesz je rozróżnić z perspektywy zdarzenia, prawdopodobnie będziesz musiał wprowadzić identyfikatory i przekazywać je wraz ze zdarzeniami (możesz użyć
reactTagnatywnego widoku jako identyfikatora).
Wywoływanie funkcji natywnych z React Native (moduły natywne)
Moduły natywne to klasy Javy/Kotlina dostępne w JS. Zazwyczaj jedna instancja każdego modułu jest tworzona na mostek JS. Mogą eksportować dowolne funkcje i stałe do React Native. Zostały szczegółowo omówione w tym artykule.
Wszystkie moduły natywne współdzielą tę samą przestrzeń nazw. Zachowaj ostrożność przy tworzeniu nowych, aby uniknąć kolizji nazw.