React.js Durum Yönetimi Eğitimini Görüntüle
Yayınlanan: 2022-03-11Front-end web geliştirmedeki en büyük ve en yaygın sorunlardan biri durum yönetimidir. Benim gibi serbest çalışan ön uç geliştiriciler, durum nesnesini görünümü ve DOM temsili ile sürekli olarak senkronize tutmaya odaklanır. Kullanıcılar uygulama ile birçok şekilde etkileşime girebilir ve bir durumdan diğerine temiz bir geçiş sağlamak büyük bir görevdir.
Biraz Tarih
Çok uzun zaman önce, web uygulamaları çok daha basit bir veri akışına sahipti. Tarayıcı sunucuya bir istek gönderir, tüm uygulama mantığı sunucuda yürütülür ve kullanıcıya sunulmak üzere tam bir HTML görünümü tarayıcıya geri gönderilir. Sonraki kullanıcı eylemleri (tıklamalar, form gönderimleri vb.) aynı akışı yeniden tetikler. Uygulamaların kullanıcı durumu hakkında endişelenmesi gerekmiyordu ve her görünüm sunucuya yeni bir istek gönderilerek yeniden oluşturulabiliyordu.
Ancak, web uygulamalarının karmaşıklığı arttı ve UI/UX'in kullanıcı talepleri de ilerliyordu. Sadece bir kısmı değiştiğinde tüm sayfayı yeniden yüklemek etkisiz ve yavaştı. Kullanıcı arayüzü üzerinde anında etki yaratan hızlı, çabuk ve duyarlı bir etkileşime ihtiyacımız vardı.
JavaScript kurtarmaya geldi. Geliştiriciler, sunucuya bir istek gönderilmeden önce tarayıcıda yürütülen önemli miktarda kodu yazmaya başladı. jQuery ayrıca, istemci tarafı doğrulama, kalıcı pencereler, uyarı mesajları, animasyonlar ve hatta Ajax tabanlı kısmi sayfa güncellemeleri gibi basit ve etkili kullanıma hazır yetenekler sağladığı için ön uç web geliştirmesine önemli ilerlemeler getirdi.
Karmaşıklığı Anlamak
Bir parolanın gücünü değerlendirmenin basit bir örneğine bakalım. Şifre uygunsa, giriş kutusu yeşil bir çerçeveye sahip olmalı ve güzel bir mesaj göstermelidir. Parola zayıfsa, giriş kutusu kırmızı bir çerçeveye sahip olmalı ve bir uyarı mesajı göstermelidir. Parola yeterince güçlü olduğunda gülen bir yüz de gösterebiliriz.
Aşağıdaki kod, bunun DOM manipülasyonu ile nasıl yapılabileceğini gösterir. Burada bir sürü “eğer” var ve kodun okunması çok kolay değil.
if (hasInputBorder()) { removeInputBorder(); } if (text.length === 0) { if (hasMessage()) { removeMessage(); } if (hasSmiley()) { removeSmiley(); } } else { var strength = getPasswordStrength(text); if (!hasInputBorder()) { addInputBorder(); } var color = (strength == 'weak' ? 'red' : 'green'); setInputBorderColor(color); var message = (strength == 'weak' ? "Password is weak" : "That's what I call a password!"); if (hasMessage()) { setMessageText(message); } else { addMessageWithText(message); } if (strength == 'weak') { if (hasSmiley()) { removeSmiley(); } } else { if (!hasSmiley()) { addSmiley(); } } }
Yukarıda gösterildiği gibi, öncelikle kullanıcının herhangi bir şifre sağlayıp sağlamadığını kontrol etmemiz ve şifre alanının boş olduğu durumu ele almamız gerekiyor. Ve her durumda, ilgili tüm DOM öğelerinin düzgün bir şekilde güncellendiğinden emin olmamız gerekir. Buna mesaj, kenarlık ve gülen yüz dahildir.
Şifre alanımız üç durumdan birinde olabilir: boş, zayıf veya güçlü. Ve belirtildiği gibi, parola alanı durumundan etkilenen üç farklı DOM öğemiz var. Tüm kombinasyonları işlemek ve görünümümüzün düzgün bir şekilde görüntülendiğinden emin olmak, bunun gibi basit bir kod parçası için bile döngüsel karmaşıklığı artırır.
DOM, tutulan modda çalışır, bu da yalnızca mevcut durumu hatırladığı anlamına gelir. Görünümümüzü değiştirmek için, her DOM öğesi için talimatlar sağlamamız ve geçişi programlamamız gerekiyor.
Durumlar yerine geçişleri kodlama karmaşık olabilir. Kodumuzda gerçekleştirmemiz gereken şube ve kontrollerin sayısı, yönetilecek görünüm durumlarının sayısıyla katlanarak artar.
Örneğimizde, bize 3 * 2 = 6
geçiş veren üç görünüm durumu tanımladık. Genel olarak, verilen N durumu, modellememiz gereken N * (N - 1) = N^2 - N
geçişe sahibiz. Örneğimize dördüncü bir durum eklersek artan karmaşıklığı bir düşünün.
Genellikle geçişlerin modellenmesiyle ilgili çok fazla kod vardır. Sadece görüş durumlarımızı tanımlayabilsek ve bir durumdan diğerine geçişin tüm detayları hakkında endişelenmesek çok daha iyi olurdu.
Karmaşıklığı Azaltma
Bir durumdan diğerine geçişi açıkça kodlamak yerine, model durumuna dayalı olarak görünüm durumunu bildirebileceğimizi varsayarsak, şöyle bir şeye sahip olabiliriz:
var strength = getPasswordStrength(text); if (text.length == 0) { return div(input({type: 'password', value: text})); } else if (strength == 'weak') { return div( input({type: 'password', value: text, borderColor: 'red'}), span({}, "Weak") ); } else { return div( input({type: 'password', value: text, borderColor: 'green'}), span({}, "That's what I call a password!"), img({class: 'icon-smiley'}) ); }
Burada, uygulamamızın üç olası durumunu temsil eden üç basit kod dalı var. Model durumuna bağlı olarak her daldaki görünümün belirtimini döndürürüz. Tüm DOM işleme kodu kaldırılır; sadece ne istediğimiz hakkında bilgi veriyoruz, oraya nasıl ulaşacağımız hakkında değil.
Bu yaklaşım, kod karmaşıklığını önemli ölçüde azaltırken, bizim adımıza gerçek DOM manipülasyonuyla ilgilenecek biri veya başka bir şey olduğunu da varsayar.
İşte burada React devreye giriyor. React, bir görünüm durumunun, temel alınan veri modelinin durumuna göre anında yönetilmesini ve güncellenmesini sağlayacaktır.
Reaksiyon
React, Facebook tarafından oluşturulan bir JavaScript kitaplığıdır. Web uygulamalarının UI bölümünü işlemek için tasarlanmıştır. Bunu MVC mimarisindeki V olarak düşünebilirsiniz. Çok odaklı. Teknoloji yığınınızın geri kalanı hakkında hiçbir varsayımda bulunmaz ve bileşenleri oluşturmaktan başka bir şeyle ilgilenmez. Genellikle daha büyük çerçevelerde bir araya getirilen herhangi bir yönlendirme mekanizması, modeli veya diğer özellikleri sağlamaz. Böylece onu karıştırabilir ve istediğiniz başka bir kütüphane veya çerçeve ile kullanabilirsiniz.
React, kullanıcı arayüzlerini bileşik bileşenlerin ağaçları olarak tanımlamamıza olanak tanır. Bir React geliştiricisi, girdi durumu verilen bileşeni tanımlayan bir işleme işlevi belirterek bu bileşenleri tanımlar. Bu işlev saf olmalıdır (yani, herhangi bir yan etkisi olmamalı veya açık girdisinden başka bir şeye bağlı olmamalıdır).
render işlevi, React'in Virtual DOM olarak adlandırdığı bir görünüm açıklaması döndürür. Bunu, işlenmiş DOM öğesine karşılık gelen bir JavaScript nesnesi olarak düşünün.
Bileşenin durumunu değiştirdiğinizde, kendisini ve tüm alt öğelerini yeniden oluşturarak yeni bir Sanal DOM döndürür.
Ayrıca, React bir durumdan diğerine geçiş yaparken basit bir HTML değişimi yapmaz. Önceki ve yeni durum arasındaki farkı bulacak ve bir geçişi gerçekleştirmek için en etkili DOM işlemleri kümesini hesaplayacaktır.
Performans göz önünde bulundurulmasa bile, kod karmaşıklığındaki azalmanın kendisi önemlidir ve çabalarımızı uygulamamızın daha benzersiz ve karmaşık bölümlerine odaklamamıza olanak tanır.
Biraz daha somut hale getirmek için, öğretici örneğimiz, görünüm durumlarını yönetmek için React kullanılarak bu şekilde yapılır.
NOT: Aşağıdaki kod örneği, React tabanlı bir UI yazmanın yaygın bir yolu olan JSX önişlemcisinde yazılmıştır.
function getPasswordStrength(text) { // Some code that calculates the strength given the password text. } var PasswordWithStrength = React.createClass({ getInitialState: function() { return {value: ''}; }, render: function() { var strength = getPasswordStrength(this.state.value); if (this.state.value.length == 0) { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} /> </div>; } else if (strength == 'weak') { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid red'} } /> <span style={{color: 'red'}}>Weak!</span> </div>; } else { return <div> <input type="password" value={this.state.value} onChange={this.handleInputChange} style={ {border: '1px solid green'} } /> <span style={{color: 'green'}}>That's what I call a password!</span> <Emoji value="smiley" /> </div>; } }, handleInputChange: function(ev) { this.setState({value: ev.target.value}); } }); React.render(<PasswordWithStrength />, document.body);
Parola gücü uygun olduğunda <Emoji value="smiley" />
ile oluşturulan Emoji
bileşeni, yalnızca başka bir özel bileşendir (tıpkı PasswordWithStrength
gibi). Şu şekilde tanımlanır:

var Emoji = React.createClass({ render: function() { var emojiSrc = this.props.value + '.png'; return <img src={emojiSrc}></img>; } });
React.js ve Diğerleri
Adil olmak gerekirse, görünüm durumu yönetimi sorununu da çözen ve hatta ona daha fazla özellik ekleyen başka istemci tarafı JavaScript çerçeveleri (Ember, Angular, Knockout ve diğerleri gibi) vardır. Öyleyse neden başka bir çerçeve yerine React'i kullanmak isteyesiniz?
Diğer kitaplıkların çoğuna kıyasla React'in sahip olduğu iki önemli avantaj vardır.
Veri Bağlama Yok
Diğer alternatif çerçevelerden bazıları, DOM öğelerini durum özelliklerine eşlemek ve özellik değişikliklerini gözlemleyerek bunları senkronize halde tutmak için veri bağlamayı kullanır. Bu yaklaşım, her değişiklikle birlikte görünümü bir kez oluşturmaya ve ardından yalnızca etkilenen DOM öğelerinin değişikliklerini tetiklemeye izin verir. Diğer alternatifler kirli kontrolü kullanır; yani, bireysel özellik değişikliklerini gözlemlemek yerine, sadece önceki durum ile yeni durum arasında bir fark gerçekleştirirler. React, ikinci yaklaşıma daha çok benzer, ancak durumları karşılaştırmak yerine görünüm temsillerini karşılaştırır.
React'in veri bağlaması yoktur. Bir geliştiricinin, durum değiştirildiğinde setState
yöntemini çağırması veya en üstteki bileşeni yeniden oluşturması beklenir. Durumdan görünüme tek yönlü bir akışı benimser.
Geliştiriciler genellikle veri bağlamayı düşünmediği için bu kavramın benimsenmesi kolaydır. Odak, verilerin görsel temsilidir. Bu nedenle bağımlı özellikler, biçimlendirme, özel HTML etiketlerinin bağlanması vb. hakkında düşünmeniz gerekmez. React ile, model değiştiğinde bileşeni yeniden oluşturmanız yeterlidir.
Buradaki görünüm durumu yönetimindeki farkı anlamak için Ember ve React'i karşılaştıralım . Tam adı büyük harfle çıkaracak bir nesne person
oluşturacağız. İki saniye sonra değişikliği simüle edeceğiz ve görünümü güncelleyeceğiz.
// EXAMPLE USING EMBER App = Ember.Application.create(); App.Person = Ember.Object.extend({ firstName: null, lastName: null, fullName: function() { return this.get('firstName') + ' ' + this.get('lastName'); }.property('firstName', 'lastName') }); var person = App.Person.create({ firstName: "John", lastName: "Doe" }); Ember.Handlebars.helper('upcase', function(value) { return value.toUpperCase(); }); App.IndexRoute = Ember.Route.extend({ model: function () { return person; } }); setTimeout(function() { person.set('firstName', 'Harry'); }, 2000); // Templates: <script type="text/x-handlebars"> <h2>Welcome to Ember.js</h2> {{outlet}} </script> <script type="text/x-handlebars" data-template-name="index"> The current user is: {{upcase model.fullName}} </script>
firstName
, lastName
ve fullName
özelliklerine sahip bir nesne oluşturduk. Ember, özellik değişikliklerini gözlemlediğinden, fullName
firstName
ve lastName
öğelerine bağlı olduğunu belirtmek zorunda kaldık. Bunu yapmak için fullName
tanımladığımızda .property('firstName', 'lastName')
ekledik.
İki saniye sonra, person.set('firstName', 'Harry');
Idam edildi. Bu, görünümün güncellenmesini ve bağlanmasını tetikledi.
Şimdi aynısını React'te yapalım.
// EXAMPLE USING REACT var CurrentUser = React.createClass({ render: function() { return <div>The current user is: {this.props.user.fullName().toUpperCase()}</div>; } }); var person = { firstName: 'John', lastName: 'Doe', fullName: function() { return this.firstName + ' ' + this.lastName; } }; var currentUser = React.render(<CurrentUser user={person}/>, document.body); setTimeout(function() { person.firstName = 'Harry'; currentUser.setProps({user: person}); }, 2000);
Ember kodu basit ve okunması kolay olsa da, React'in basitlikte kazandığı açıktır. person
, fullName
yalnızca bir işlev olan düz bir JavaScript nesnesidir.
Şablonlama Yok
Her alternatif çerçevenin şablonları işlemek için farklı bir yolu vardır. Bazıları JavaScript'te derlenen dizeleri kullanırken diğerleri doğrudan DOM öğelerini kullanır. Çoğu, daha sonra HTML olarak "derlenen" özel HTML niteliklerini ve etiketlerini kullanır.
Şablonlar, JavaScript kodunun parçası değildir. Bu nedenle, her alternatifin ortak işlemleri, koşulları, yinelemeleri, çağrı işlevlerini vb. temsil etmek için özel bir yola ihtiyacı vardır. Bunların tümü, geliştiricilerin öğrenmesi gereken yeni bir sözde dil oluşturur.
React'te şablonlama yoktur, her şey sadece düz eski JavaScript'tir.
React, görünümü oluşturmak için JavaScript'in tüm gücünü kullanır. Bileşenin oluşturma yöntemi bir JavaScript işlevidir.
JSX, "HTML benzeri sözdizimini" normal JavaScript'e dönüştüren bir ön işlemci olarak mevcuttur, ancak JSX isteğe bağlıdır ve herhangi bir ön işlemci olmadan standart JavaScript'i kullanmakta özgürsünüz. Ayrıca mevcut JavaScript araçlarından da yararlanabilirsiniz. Linterler, ön işlemciler, tip açıklamaları, küçültme, ölü kodun ortadan kaldırılması vb.
React'i görünüm durumu yönetimi için alternatif çerçevelerden biriyle karşılaştırmak için yine somut bir örnek kullanalım.
Aşağıdaki öğretici, hashtag'leri ve her biri için tweet sayısını listelemek için AngularJS kullanımına bir örnektir. Liste sayıya göre sıralanır ve hashtag yoksa bir mesaj gösterilir.
<!-- EXAMPLE USING ANGULAR --> <div ng-controller="MyCtrl"> <ul ng-show="hashTags.length > 0"> <li ng-repeat="hashTag in hashTags | orderBy:'tweetCount':true"> {{hashTag.name}} - {{hashTag.tweetCount}} tweets </li> </ul> <span ng-show="hashTags.length == 0">No hashtags found!</span> </div>
Bu listeyi yapabilmek için bir geliştiricinin AngularJS directives
, ng-show
ve ng-repeat
hakkında bilgi sahibi olması gerekir. Ardından orderBy
anlamak için AngularJS filters
öğrenmesi gerekiyor. Bir liste çıktısı almak gibi basit bir şey için çok iş!
Şimdi aynı şeyi yapan React örneğini ele alalım:
// EXAMPLE USING REACT function byTweetCountDesc(h1, h2) { return h2.tweetCount - h1.tweetCount; } //... render: function() { if (this.state.hashTags.length > 0) { var comps = this.state.hashTags.sort(byTweetCountDesc).map(function(hashTag, index) { return <li key={index}> {hashTag.name} - {hashTag.tweetCount} tweets </li>; }); return <ul>{comps}</ul>; } else { return <span>No hashtags found!</span> } }
“Daha gelişmiş” yaklaşımı ve JSX'i kullansak bile, temel JavaScript anlayışına sahip her web geliştiricisi yukarıdaki kodu kolayca okuyabilir ve ne yaptığını anlayabilir. if
kullanarak standart koşullu kontrol, map()
kullanarak yineleme ve standart bir 'sort()' her geliştiriciye doğal olarak gelir, bu nedenle öğrenilecek fazladan sözdizimi veya başka kavramlar yoktur.
Çözüm
Bu React.js öğreticisinin ana çıkarımı, React'in geçişler yerine gerçek görünüm durumu yönetimine odaklanmanıza olanak sağlaması ve böylece işinizi ve uygulamanızı basitleştirmesidir.
React'i benimsemek için öğrenme eğrisi oldukça önemsizdir. Uzmanlaşacak özel şablonlama dili yok, endişelenecek veri bağlama yok ve her şey UI öğelerini tanımlayan JavaScript işlevlerine bağlı.
Uygulama kodunuzu React kullanarak basitleştirme hakkında daha fazla bilgi edinmek için Steven Luscher'ın Decomplexifying Code with React başlıklı konuşmasına bir göz atın.
İşte bir sonraki adımı atmak ve React'i kullanmaya başlamak isteyenler için bazı ek okumalar:
- http://jlongster.com/Removing-User-Interface-Complexity,-or- Why-React-is-Awesome