React Context API

2019-04-27

Eğer React ile uygulama geliştiriyorsanız, komponentler arası veri aktarımı yaparken props drilling yöntemine illaki başvurmuşsunuzdur. Az sayıda komponentten meydana gelen bir uygulamada önerilen yöntem de bu zaten.

Geliştirdiğiniz uygulamada komponent sayısı gittikçe artıyorsa, veriyi bir yerden alıp başka bir yere göndermek de bununla birlikte paralel olarak zorlaşır. Bu gibi durumlarda verinizi tutabileceğiniz ve herhangi bir yerden ulaşabileceğiniz bir yapıya ihtiyaç duyarsınız. React Context API işte tam da burada bizim kurtarıcımız oluyor.

Context yapısı sayesinde veriyi her yerden ulaşabileceğimiz bir Provider içerisinde muhafaza edebiliriz ve daha sonra ihtiyaç duyduğumuz yerde o veriye Consumer ile ulaşırız.

Makalenin geriye kalan kısmında create-react-app ile oluşturulmuş bir proje varsayılarak devam edeceğiz. Bu proje içerisinde src dizini içerisinde sadece index.js dosyası tutulmuş diğer her şey silinmiş farz edelim.

Context

Provider ve Consumer ilişkisini kurmadan önce ihtiyaç duyduğumuz şey aslında bir Context belirlemek. Projemizin src dizini içerisinde context.js diye bir dosya oluşturduktan sonra ilk önce React'ı dosyamıza dahil edip daha sonra da hemen aşağıdaki gibi Context'i oluşturalım.

export const Context = React.createContext();
view raw context.js hosted with ❤ by GitHub

Provider

Şimdi oluşturduğumuz Context'in hemen altında Provider'ı oluşturabiliriz. Bunu yapmak hemen hemen başka bir komponent yazmaktan farksız. Bu komponent geliştirdiğimiz bütün uygulamayı sarmalayacak bir higher order component. Oluşturduğumuz komponentin state'ini basitçe props olarak aşağıya göderiyor olacağız. Dolayısıyla Provider içerisindeki state merkezi bir yapı gibi olacak ve istenilen yerde kullanılabilecek.

export class Provider extends Component {
state = {
name: 'Ufuk',
age: 22,
};
render() {
return (
<Context.Provider value={{
state: this.state
}}>
{this.props.children}
</Context.Provider>
)
}
}
view raw context.js hosted with ❤ by GitHub

Şimdi src dizinin içerisine Parent adında bir komponent oluşturalım. Bu komponent index.js içerisinde sayfaya render ettiğimiz ana komponent olsun. Hemen ardından da yazdığımız Provider ile sayfaya render edeceğimiz bütün her şeyi sarmalayalım. Tekrar ediyorum bu komponent index.js dosyasında sayfaya render edilen ana komponent olacak.

export default class Parent extends Component {
render() {
return (
<Provider>
<Child />
</Provider>
);
}
}
view raw Parent.js hosted with ❤ by GitHub

Consumer

Şimdi sıra geldi Provider ile bütün uygulamamızın her bir köşesine sağladığımız veriyi Consumer yardımıyla tüketmeye...

Parent komponenti içerisindeki Child komponenti içerisinden Context'imizi dahil etmiş olmalıyız ki daha sonra render metodu içerisinden kullanabilelim. Burada render props denilen bir yapı kullanıldığını hatırlatmadan geçmeyeyim.

export default class Child extends Component {
render() {
return (
<Context.Consumer>
{value => (
<div>
<h1>{value.state.name}</h1>
<p>{value.state.age}</p>
</div>
)}
</Context.Consumer>
);
}
}
view raw Child.js hosted with ❤ by GitHub

Yukarıda gördüğünüz yapıyı da kurguladıktan sonra development server'ımızı çalıştıralım. Sonuç olarak name ve age verilerine kolaylıkla ulaşabildiğimizi görüyor olmalıyız.

Verileri güncelleme

Peki ya Child komponenti içerisinden oluşturduğumuz global state'deki verilerden bazılarını değiştirmeye ihtiyaç duysaydık ne yapıyor olurduk. Aslında bunun için de bilindik bir yönteme başvuracağız. Hatırlarsanız bizim Provider komponentimiz tıpkı yazdığımız diğer komponentlerden farksızdı, dolayısıyla içerisine istediğimiz işlemi yapan bir fonksiyon yazmak hiç de zor olmayacaktır. Daha sonra da bu fonksiyonu uygulamamızın her yerinden kullanılabilmesi için props olarak göndermeliyiz.

export class Provider extends Component {
state = {
name: 'Ufuk',
age: 22,
};
incrementAge = () => {
this.setState({
age: this.state.age + 1
})
};
render() {
return (
<Context.Provider value={{
state: this.state,
incrementAge: this.incrementAge
}}>
{this.props.children}
</Context.Provider>
)
}
}
view raw context.js hosted with ❤ by GitHub

Yukarıda gördüğünüz gibi context.js dosyamız içerisindeki Provider komponentine bir fonksiyon yazdık ve onu aşağıya yani sarmaladığımız diğer komponentlere kullanılması için props olarak gönderiyoruz.

Tabi bu fonksiyonu Child komponentinden kullanabilmek için bir buton eklememiz lazım. Daha sonra da butona event listener eklememiz yeterli olacaktır.

export default class Child extends Component {
render() {
return (
<Context.Consumer>
{value => (
<div>
<h1>{value.state.name}</h1>
<p>{value.state.age}</p>
<button onClick={value.incrementAge}>Increment</button>
</div>
)}
</Context.Consumer>
);
}
}
view raw Child.js hosted with ❤ by GitHub

Sonuç olarak uygulamanıza ne kadar çok komponent eklerseniz ekleyin Provider tarafından sarmalanan bütün komponentler Consumer yardımıyla context'teki verilere ulaşabiliyor ve güncelleyebiliyor.

Uygulamanın tam olarak kaynak kodlarını görmek istiyorsanız şöyle buyrun efendim.