Przy okazji projektu DevMovies postanowiłem wypróbować bibliotekę ReactJS.NET. O samym projekcie więcej możecie się dowiedzieć tutaj. W tym poście znajdziecie kilka przemyśleń i uwag dotyczących ReactJS.NET zebranych w trakcie realizacji projektu.
W ramach krótkiego wyjaśnienia: ReactJS.NET to biblioteka napisana w C# ułatwiająca korzystanie z javascriptowej wersji ReactJS w projektach dotnetowych np. ASP.NET MVC. React.JS sam w sobie został stworzony przez Facebooka i służy do generowania UI oraz implementowania interakcji na zachowania użytkownika. Więcej na temat samej biblioteki dowiecie się ze strony projektu (świetna dokumentacja i przykłady), sesji pt. React + NPM for Great Good (Rob Ashton) z tegorocznego Dev Daya (warto obejrzeć) i z podcastu pt. O Javascript z Jakubem Gutkowskim (gorąco polecam wszystkim, którzy jeszcze nie mieli okazji posłuchać).
W pierwszej kolejności postanowiłem napisać o plusach ReactJS. Przede wszystkim to, co mi się spodobało w tej bibliotece to prostota. Mało konfiguracji i szeroko pojętego bojlerplejtu żeby w pełni korzystać z dobrodziejstw biblioteki to zdecydowana zaleta. Dodatkowo biblioteka nie posiada zbędnych fajerwerków dzięki czemu całość można ogarnąć w jeden wieczór a w dwa śmigać bez dokumentacji ;). Po za tym biblioteka działa i robi dokładnie to, do czego została stworzona, co również uważam za plus bo z tym bywa różnie.
Niestety w trakcie zabawy z ReactJS dostrzegłem także kilka minusów. Po pierwsze brak wsparcia ze strony Visual Studio. Mimo tego, że składnia React to czysty Javascript z dodatkiem HTMLa (z drobnymi modyfikacjami) to Visual Studio nie pokoloruje nam kodu. Według mnie to dość poważnym minus. W tej chwili nawet instalacja dodatku Web Essentials nie załatwia sprawy ponieważ temat wsparcia dla React wisi od roku i pozostaje otwarty. W tej sytuacji jedynym rozwiązaniem jest skorzystanie z innego edytora do pracy z plikami *.jsx. Ja postawiłem na Sublime z dodatkiem sublime-react. W rezultacie sublime zaczął rozpoznawać składnię a dodatkowo ułatwił pracę dzięki zestawowi snippetów.
Zwykle cały szablon dzielimy na kawałki (komponenty). Jeżeli taki kawałek chcemy przemielić przy pomocy React to niezależnie od tego co dany fragment reprezentuje (obrazek, listę wypunktowaną, całe menu) musimy go otoczyć tagiem div. Niby nic, jednak czasem może to być problematyczne i wymusi na nas stosowanie hacków. Przykład z życia: chcę wyrenderować dostępne opcje sortowania. Opcje mają być wyświetlone w postaci dropdowna. Dlatego, że korzystam z Bootstrapa muszę to wyrenderować jako listę wypunktowaną i ubrać w odpowiednie klasy:
1 2 3 4 5 6 7 8 9 |
<del><ul className="dropdown-menu"> <li> opcja 1</li> <!-- ... --> <li>opcja n </li> </ul></del> |
Kod ReactJS realizujący powyższe założenie:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<del>var options = this.props.orders.map(function(field){ var active = field === this.state.current.field; return (<div> <li className="divider"></li> <li className={active && this.state.current.direction == this.props.directions.ascending ? 'active' : '' }> <a href="#" onClick={this.sortOrderChanged} data-order-direction={this.props.directions.ascending} data-field={field.field}>{field.label} <span className="glyphicon glyphicon-sort-by-attributes"></span></a> </li> <li className={active && this.state.current.direction == this.props.directions.descending ? 'active' : '' }> <a href="#" onClick={this.sortOrderChanged} data-order-direction={this.props.directions.descending} data-field={field.field}>{field.label} <span className="glyphicon glyphicon-sort-by-attributes-alt"></span></a> </li> </div>); }.bind(this));</del> |
Niestety szablon nie zostanie wyświetlony w przeglądarce prawidłowo. Problem wynika stąd, że klasa Bootstrapa dla elementów <li>
jest zdefiniowana w ten sposób:
1 2 3 |
<del>ul.dropdown-menu > li{ /* styl */ }</del> |
W tym przypadku styl zdefiniowany w klasie będzie zastosowany tylko dla bezpośrednich dzieci elementu nadrzędnego jakim jest tag <ul>
. Niestety w kodzie źródłowym wspomnianej listy wygenerowanym przez React znajdziemy coś takiego:
1 2 3 4 5 |
<del><ul class="dropdown-menu"> <div><li>Opcja 1</li></div> <!-- ... --> <div><li>Opcja n</li></div> </ul></del> |
Jak widać tutaj bezpośrednim dzieckiem elementu <ul>
jest <div>
. W rezultacie style przygotowane przez zespół Bootstrapa nie zostaną zastosowane. Rozwiązania problemu są co najmniej dwa:
Poprawiamy styl źródłowy usuwając>
lub dodając> div
dla odpowiednich definicji klas.Kopiujemy klasy zdefiniowane w bootstrap.css do naszego pliku ze stylami i nanosimy poprawki w definicji klas opisane w punkcie 1.
Osobiście nie podoba mi się żadne z rozwiązań. Rozwiązanie pierwsze jest ryzykowne jeżeli Boostrapa zainstalowaliśmy korzystając z Nugeta lub Bowera. Jeżeli paczka zostanie na nowo pobrana wtedy utracimy wprowadzone poprawki. Drugie rozwiązanie nie podoba mi się z tego powodu, że musimy przekopiować N linii stylów tylko po to to żeby zastosować mały fix.
Dodatkowym minusem jaki dostrzegłem w trakcie mojej krótkiej przygody z ReactJS.NET to problem z devtoolami w Chrome. Przy kilku komponentach i otwartych devtoolsach niejednokrotnie wywaliła mi się cała karta i jedyne co mogłem zrobić to nacisnąć F5. Po kilku wysypkach zaczęło to być bardzo denerwujące. Dodatkowo kilka razy zdarzyło mi się, że wysypał mi się IIS Express w trakcie debugowania aplikacji. Dostawałem błąd AccessViolationException
co nie brzmi dobrze i pozwala mi sądzić, że coś jest skopane od strony server side w implementacji ReactJS.NET. Co było nie tak – nie wiem. Szkoda mi było czasu na eksplorowanie sieci w poszukiwaniu potencjalnych przyczyn.
Podsumowanie
Ogólnie zabawę z ReactJS uważam za ciekawą przygodę i w zasadzie tyle jeżeli chodzi o pozytywy. Jeżeli ktoś ma kilka wolnych wieczorów i chciałby się pobawić czymś nowym to polecam, czemu nie. Problemy z jakimi się spotkałem były na tyle upierdliwe, że prawdopodobnie nie skorzystałbym z Reacta po raz kolejny. Co w zamian? W zamian sięgnąłbym po KnockoutJS i resztę zostawił Razorowi. Nie wykluczam, że w przyszłości w projekcie DevMovies zrezygnuję z Reacta i całość przepiszę korzystając właśnie z pary KnockoutJS+Razor.
EDIT:
Dzięki sugestiom w komentarzach pochyliłem się raz jeszcze nad powyżej opisanym problemem i faktycznie, Piotr Perak miał rację! W związku z tym uważam, że React jest jednak bardziej “in plus” niż “in minus”. Nie zmienia to faktu, że prawdopodobnie w przyszłych projektach i tak skorzystam z KnockoutJS+Razor :).
Mi React bardzo się podoba. Zrobiłem na nim jeden z głównych komponentów naszej aplikacji – rozwijalne menu dostępne na każdym ekranie. Nie miałem problemów z dev toolsami w Chrome. Brak wsparcia przez VS to również żaden problem. I tak edytuję jsx-y w GVIM-ie. Co do render to nie ma wymagania, aby zwracać div-a na najwyższym poziomie. Wymaganiem jest, aby zwracać jeden element na najwyższym poziomie. A w twoim przypadku wogóle ten div jest niepotrzebny. W przedstawionym kodzie budujesz tylko liste options. A zwracasz ją dalej otoczoną div-em w linii 69 – https://github.com/tkestowicz/DevMoviesPL/blob/master/TheDevelopersStuff/TheDevelopersStuff.Web.UI/Scripts/components/videosList.jsx#L69
Devtoolsy to może być problem lokalny, fakt. VS to kwestia gustu, owszem. Co do tego: “Co do render to nie ma wymagania, aby zwracać div-a na najwyższym poziomie.”. Muszę to sprawdzić, jeżeli to prawda to w takim układzie treść Message w wyjątku jest myląca.
Ja mam komponenty, które zwracają a href… Zresztą dla tego kodu powyżej to nie ma znaczenia. Budując listę options możesz tam wrzucić tylko LI ponieważ ta lista nie jest zwracana z render bezpośrednio. Jest otaczana dalej div-em.
Dzięki za tip! Usiądę do tego raz jeszcze i może bardziej się przekonam 🙂