", line 149
182 | elif isinstance(s, bytes):
183 | ^^^^
184 | SyntaxError: invalid syntax
185 | make: *** [Python/frozen_modules/genericpath.h] Error 1
186 |
187 | ```
188 |
189 | 200 IQ hareket . `elif` i değiştik ama Python internal modullerinde hala `elif` kullanıyor. Dolayısıyla parser hata çıkartıyor.
190 |
191 | Bunun çözmek için `elif` keywordunu tamamen değişmek yerine `elseif` i de kullanılabilir yapmak gerekiyor. Dokümantasyonda biraz gezindikten sonra
192 | `|` operatoru ile bunu yapabileceğimi anlıyorum. Aynı yerlerdeki `'elseif'` stringlerini `('elif' | 'elseif')` bununla değiştiriyorum. `make regen-pegen`
193 | ve `make -s -j8` komutunu çalıştırdığımızada.
194 |
195 | ```bash
196 | ~/cpython on main ⇡1 !2
197 | ❯ ./python.exe
198 | Python 3.11.0a2+ (heads/main-dirty:36e1a69ba8, Nov 26 2021, 19:37:05) [Clang 12.0.5 (clang-1205.0.22.11)] on darwin
199 | Type "help", "copyright", "credits" or "license" for more information.
200 | >>> if True:
201 | ... pass
202 | ... elseif False:
203 | ... pass
204 | ...
205 | >>>
206 | ```
207 |
208 | Bingo!!
209 |
210 | Artık rahatlıkla Python kullanabilirsiniz.
211 |
212 | ### Kaynaklar
213 |
214 | - https://devguide.python.org/grammar/
215 | - https://realpython.com/cpython-source-code-guide/
216 | - https://isidentical-archive.github.io/cpython-yeni-operator.html
217 |
--------------------------------------------------------------------------------
/pages/posts/artik-pythonda-elif-yazmak-zorunda-degilsiniz/tw_image.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/artik-pythonda-elif-yazmak-zorunda-degilsiniz/tw_image.jpg
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/img.png
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/img_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/img_1.png
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/img_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/img_2.png
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/img_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/img_3.png
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/img_4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/img_4.png
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/img_5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/img_5.png
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/img_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/img_6.png
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Bu Siteyi Nasıl Oluşturdum (eski)
3 | date: 2022-03-16
4 | description: Bu yazıda bu web sitesini Svelte ve SvelteKit ile nasıl oluşturduğumu anlayacağım.
5 | tag:
6 | - svelte
7 | - svetlekit
8 | - ssg
9 | ---
10 |
11 | import { Callout } from 'nextra/components'
12 |
13 |
14 | Bu yazi suan goruntulediginiz web sitesinin eski versiyonu icin yazilmistir. Sıkıldığım için Next.js tabanli [Nextra](https://nextra.site/) ile tekrar olusturdum 🤷♂️
15 |
16 |
17 | Uzun zamandır aklımda kolayca içerik üretebileceğim, hızlı,
18 | sade bir blog yapmak vardı. Pek çok farklı şekilde iyi-kötü(çoğunlukla kötü)
19 | kendim için blog sayfaları oluşturmuştum. Backendini kendim yazdığım da oldu,
20 | sadece frontendini yazıp backendini [Strapi](https://strapi.io/)'ye verdiğim de. Ama hep
21 | bir şeyler istediğim gibi olmuyordu. Bir türlü ihtiyaçlarımı tam anlamıyla
22 | karşılayacak bir web sitesi yapamamıştım kendime. Ama son 1 haftadır
23 | Svelte ve SvelteKit ile istediğim siteyi oluşturduğumu düşünüyorum.
24 | Bu yazıda bu web sitesini _Svelte_ ve _SvelteKit_ ile nasıl oluşturduğumu
25 | anlatacağım
26 |
27 | ## İhtiyaçlarım
28 |
29 | Websitesini yapmaya girişmeden önce ihtiyaçlarımı belirlemeye karar verdim.
30 | Bunlar:
31 |
32 | - Olabildiğince sade bir tasarım.
33 |
34 | Websitemin daha çok içeriğe odaklı olması
35 | gerektiğini düşündüğüm için, içeriği ön plana alacak şeklide bir
36 | tasarım düşündüm. [Bu](https://antfu.me/),
37 | [bu](https://www.aleksandrhovhannisyan.com/) ve
38 | [bu](https://ademilter.com/) sitelerden ilham aldığım söylenebilir.
39 |
40 | - Hız.
41 |
42 | Yaptığım şey altı üstü blog post paylaşmak. Bu dönemde böyle bir iş
43 | çok fazla kaynak tüketmese gerek. Dolayısıyla sitenin ve sayfaların açılış
44 | hızı olabilidiğince hızlı olmalı.
45 |
46 | - Hızlıca içerik üretebilme.
47 |
48 | Önceki denediğim sistemlerde, bir şeyler paylaşmak
49 | hiç kolay değildi. Kolaycadan kastım şu: Aklıma gelen veya paylaşmak
50 | istediğim bir şeyi telefonda olsam bile yazıp tek tuşla yayına almak.
51 |
52 | - Bol özellikli makale yazma formatı.
53 |
54 | Bir yazılım geliştirici olduğum için aşına olduğum metin yazma formatı
55 | `markdown`. Ama sadece bu da yetmiyor, yeri geliyor makalede interaktif
56 | bir şeyler yapabilmek istiyorum. Ayırca kod formatlama, syntax highlighting
57 | de gelişmiş olmalı.
58 |
59 | ## Neler kullandım
60 |
61 | - [Svelte](https://svelte.dev/) - _Kullandığım frontend framework_
62 | - [SvelteKit](https://kit.svelte.dev/) - _Svelte ile birlikte çalışan, server-side-rendering (SSR),
63 | static-site-generator (SSG) için framework_
64 | - [Tailwind](https://tailwindcss.com/) - _CSS framework_
65 | - [MDsveX](https://mdsvex.pngwn.io/) - _Markdown ve SvelteKit ile birlikte çalışan, markdown dosyalarını
66 | process eden kütüphane_
67 | - [Notion.so](https://www.notion.so) - _[snippets](/snippets) bölümü için kullandığım platform_
68 | - [Upstash](https://upstash.com/) - Sayfaların ne kadar görüntülenme aldığını tutmak için `key-value`
69 | database
70 | - [Raindrop.io](https://raindrop.io/) - _[bookmarks](/bookmarks) bölümü için kullandığım platform_
71 |
72 | ## SvelteKit ve SSG(Static Site Generator) nasıl çalışıyor?
73 |
74 | Bir SvelteKit uygulaması oluşturduğunuzda, `/routes` adlı bir dizinle
75 | gelir. Bu dizin içindeki dosyalara göre web sitenizin yolları belirlenir.
76 |
77 | 
78 |
79 | Bu sayfalar ayrıca static olarak `.html` dosyalarına dönüştürülebilir. Eğer
80 | `svete.config.js` dosyasında `kit.prerender.default = true` yaparsanız,
81 | varsayılan olarak `/routes` içindeki dosyaları statik olarak çalışmasını
82 | sağlarsınız. `build` komutunu çalıştırmanız yeterli olur.
83 |
84 | 
85 |
86 | Eger backend'e işiniz düştüyse bunu bir endpoint yardımıyla çözebilirsiniz.
87 |
88 | ### Endpoints
89 |
90 | SvelteKit uygulamalarınızda kendinize özel api uçları tasarlayabilirsiniz.
91 | Bu uçlarda çalışacak kodlar backend içinde çalışır. Ben tamamen static
92 | bir site yapmak istediğim için bu uçları, build alınırken çalıştırıp
93 | sonuçlarını static olarak dosyalara yazacağım... Yani SvelteKit yazacak.
94 |
95 | Bir endpoint içine temel HTTP methodları için ayrı ayrı fonksiyonlar
96 | yazılabilir. Bunlar:
97 |
98 | ```js
99 | export function get(event) {...} // GET
100 | export function post(event) {...} // POST
101 | export function put(event) {...} // PUT
102 | export function patch(event) {...} // PATCH
103 | export function del(event) {...} // DELETE
104 | ```
105 |
106 | Benim işime şuan `GET` methodu yaradığı için `get` fonskiyonunu yazacağım
107 | sadece.
108 |
109 | ```ts:/routes/api/bookmarks.json.ts
110 | import type { RequestHandler } from '@sveltejs/kit';
111 | import variables from '$lib/variables';
112 |
113 | const RAINDROP_URL = 'https://api.raindrop.io/rest/v1/raindrops/0?perpage=30';
114 |
115 | export const get: RequestHandler = async () => {
116 | const bookmarks = await (
117 | await fetch(RAINDROP_URL, {
118 | headers: {
119 | Authorization: `Bearer ${variables.RAINDROP_API_KEY}`
120 | }
121 | })
122 | ).json();
123 |
124 | return {
125 | body: {
126 | bookmarks
127 | }
128 | };
129 | };
130 | ```
131 |
132 | Burada [Raindrop](https://raindrop.io/) ile kişisel bookmarklarımı çekiyoum. Ardından
133 | aldığım değeri olduğu gibi endointden dönüyorum.
134 |
135 | Sonrasında tarayıcımda `/api/bookmarks.json`'a gidersem dönen değeri
136 | görebilirim.
137 |
138 | 
139 |
140 | Burada dikkat etmem gereken bu endpointin build zamanda çalışması için
141 | dosya ismin `*.json.{js,ts}` şeklinde yazmış olmam. Sveltekit bu formatı
142 | anlayıp build esnasında bu dosyayı çalıştırıyor ve sonuçlarını
143 | `bookmarks.json` olarak bir dosyaya yazıyor.
144 |
145 | Şimdi bunu sayfa componentimin içinde nasıl göstereceğim?
146 |
147 | Svelte bunun için bana güzel bir API sunmuş.
148 | Componentin server tarafında çalışmasını
149 | istediğim kodlarını `` tagları
150 | arasına yazarsam, yazdığımız endpoint ile haberleşebilirim.
151 |
152 | ```svelte:/routes/bookmarks.svelte
153 |
170 |
171 |
174 |
175 | Bookmarks
176 |
177 |
178 | {#each bookmarks.items as bookmark}
179 |
180 |
{bookmark.title}
181 |
182 | {/each}
183 | ```
184 |
185 | Bu sitedeki [`/bookmarks`](/bookmarks) yolundan bu kodların çıktısına
186 | bakabilirsiniz.
187 |
188 | Aynı şekilde [`/snippets`](/snippets) kısmını da bu yolla yaptım. Aynı
189 | şeyleri yaptığım için anlatmaya gerek duymuyorum. [Notion.so](https://www.notion.so/)'dan gelen
190 | verileri parse ettiğim sıkıcı kodlar var sadece :)
191 | İsteyen [buradan](https://github.com/bufgix/website/blob/master/src/routes/api/snippets/index.json.ts) bakabilir
192 |
193 | ### Blog Postlar ve MDsveX
194 |
195 | Peki, blog postlar için de `.svelte` dosyaları mı kullanıyorum?
196 | Tabii ki hayır. Bunun için [MDsveX](https://mdsvex.pngwn.io/) markdown processor kullanıyorum.
197 | Yaptığı şey basitçe `/routes` içinde bir `.md` dosyası varsa bunu parse
198 | edip aynı bir svelte sayfası gibi göstermek. Bunu yaparken bol
199 | eklenti desteği, metadata, layouts, syntax highlighting gibi pek çok
200 | özelliğini de kullanbiliyorsunuz.
201 |
202 | 
203 |
204 | Kullandığım eklentiler ise şu şekilde:
205 |
206 | ```js
207 | import relativeImages from 'mdsvex-relative-images';
208 | import remarkHeadingId from 'remark-heading-id';
209 | import figure from 'rehype-figure';
210 | import codeTitle from 'remark-code-titles';
211 |
212 | mdsvex({
213 | extensions: ['.md', '.svx'],
214 | remarkPlugins: [relativeImages, remarkHeadingId, codeTitle],
215 | rehypePlugins: [figure],
216 | });
217 | ```
218 |
219 | Bu websitesinin kodlarına açık kaynak olarak [Github](https://github.com/bufgix/website)'dan erişebilirsiniz.
220 |
221 | ### Kaynakça
222 |
223 | - https://joshcollinsworth.com/blog/build-static-sveltekit-markdown-blog
224 | - https://kit.svelte.dev/
225 |
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/mdsvex.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/mdsvex.png
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/svelte_proc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/svelte_proc.png
--------------------------------------------------------------------------------
/pages/posts/bu-siteyi-nasil-olusturdum/svelte_routing.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/bu-siteyi-nasil-olusturdum/svelte_routing.png
--------------------------------------------------------------------------------
/pages/posts/building-bulletproof-react-native-applications/design1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/building-bulletproof-react-native-applications/design1.png
--------------------------------------------------------------------------------
/pages/posts/building-bulletproof-react-native-applications/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Kurşun Geçirmez React Native Uygulamaları Oluşturmak - I
3 | date: 2022-01-01
4 | description: Bu yazıda gelir getirmesi istenen bir uygulamanın uygulama-içi-satın-alma, abonelik, metadata, build aşamaları, markete vermeden update etme gibi konuları ele alacağım.
5 | tag:
6 | - react
7 | - react-native
8 | - typescript
9 | - redux
10 | ---
11 |
12 | Evet biliyorum _clickbait_ başlık ama son zamanlarda (_7 ay_) React Native
13 | ile baya bi' uygulama geliştirdim. Bu süreçte öğrendiğim bazı şeyleri paylaşmak istiyorum.
14 | Burada anlatacağım her şey belki en doğru yöntem değil fakat React Native projelerimi
15 | bu şekilde oluşturuyorum ve şimdiye kadar sorun yaşamadım.
16 |
17 | Bu yazıda gelir getirmesi istenen bir uygulamanın
18 | _uygulama-içi-satın-alma_, _abonelik_, _metadata_, _build aşamaları_, _markete vermeden update etme_ gibi
19 | konuları ele alacağım. Bunun yanında React tarafında
20 | _store yapısı, hookların efektif kullanımı, API dizaynı_ gibi başlıklardan da bahsetmek istiyorum.
21 |
22 | ## Projeyi Oluşturmak
23 |
24 | Bir React Native uygulaması oluşturmanın birden fazla yolu var. Bunlardan biri
25 | [Expo](https://expo.dev/) kullanmak. Expo kullanmak yeni başlayan biri için oldukça kolay bir yol olabilir.
26 | Fakat kişisel olarak ben artık tercih etmiyorum nedenleri ise:
27 |
28 | - Uygulamanın boyutunu fazla **büyütmesi** (Bir QR kod okuyucu yapmıştım ve production
29 | buildi 67mb olmuştu :D. Bunu azaltmanın yolları var tabi ama o toplara girmeyi tercih etmiyorum)
30 | - Bazı native modulleri Expo üzerinden **kullanamamak**.
31 |
32 | Bunlar dışında çok da yok aslında ama normal React Native kurulumuyla başlamak bana
33 | daha fazla kontrol sağlıyor. (Expo kullanıp memnun olanlar da var tabi. [Buradan](https://twitter.com/enesozt_/status/1474039877893296140)
34 | okuyabilirsiniz. Anlaşılan Expo'nun yeni sürümleri yukarda bahsettiğim sorunları çözüyor. Yakında yeniden denerim kendisini.)
35 |
36 | #### Typescript
37 |
38 | Evet bu [zımbırtı](https://www.typescriptlang.org/) olmadan ben artık uygulama _yazmıyorum_. İlerde anlatacağım çoğu şeyde Typescript
39 | yardımını kullandığım için Typescriptin olmaması kabul edilemez. Unutmayın amacımız kurşun geçirmeyen,
40 | sürdürülebilir bir uygulama oluşturmak. Typescript bu konuda baya kolaylik sağlıyor.
41 | bu yüzden React Native uygulamasını Typescript ile kurmanızı tavsiye ederim.
42 |
43 | ```shell
44 | $ npx react-native init BestAppEver --template react-native-template-typescript
45 | ```
46 |
47 | Detaylı kurulumu tabii ki React Native'in [kendi sitesinden](https://reactnative.dev/docs/environment-setup)
48 | bulabilirsiniz.
49 |
50 | Ayrıca ilerde proje içi tipler için bir `*.d.ts` dosyanızı oluşturmanızı tavsiye ederim.
51 |
52 | ```ts filename="./src/types/index.d.ts"
53 | declare global {
54 | type MyType = {
55 | name: string;
56 | //...
57 | };
58 | }
59 | ```
60 |
61 | Bu dosyayı oluşturduktan sonra import etmeye gerek kalmadan tiplerinizi kullanabileceksiniz. Fazlalık
62 | importların oluşmasını önlemek için güzel bir yöntem.
63 |
64 | ### Dizin Yapısı
65 | import { FileTree } from 'nextra/components'
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 | Genel olarak böyle bir dizin yapısı kullanıyorum. Kısaca bir özet geceyim, daha sonra daha detaylı inceleyelim.
128 |
129 | - `api` burada backend servisi ile haberleşmek için gerekli dosyalar bulunur. `Axios`, `Apollo` gibi kütühanelerin örneklemeleri burada bulunur
130 | - `assets` burada uygulamada kullanılacak resimler ve fontlar gibi static dosyalar bulunur
131 | - `components` burada uygulama bileşenleri bulunur
132 | > Tercihen ben bu dizin içinde bir de `index.ts` ekleyip tüm componentleri buradan exportluyorum. Bu, komponenti
133 | > kullanırken daha temiz bir kod sağlıyor
134 | >
135 | > ```ts
136 | > export { default as Page } from './Page';
137 | > export { default as Text } from './Text';
138 | > export { default as Card } from './Card';
139 | > export { default as Button } from './Button';
140 | > ```
141 | >
142 | > kullanırken
143 | >
144 | > ```ts
145 | > import { Button } from '@/components';
146 | > ```
147 | - `icons` Burada svg tipinde iconlar bulunur.
148 | - `lang` Burada dil objeleri bulunur
149 | - `navigation` Burada sayfaların navigasyondaki yollarını belirleyen tanımlamalar bulunur
150 | - `store` Burada uygulamanın store dosyaları bulunur
151 | - `styles` Burada renkler, fontlar ve margin-padding değerleri bulunur.
152 | - `typings` Burada uygulamamız için tip tanımlamaları bulunur
153 | - `utils` Burada hooklar, yardımcı methodlar ve sabit değelerin olduğu dosyalar bulunur
154 | - `views` Burada uygulamada kullancağımız sayfalar bulunur
155 | - `bootstrap.tsx` Bu dosya uygulamamızın kök komponenti. Genelde burada uygulama açılmadan önce yüklencek
156 | asenkron işlemler(network requestleri, local databaseden inital verilerin çekilmesi vb.) yapılır. Provider'ları da
157 | bu dosyaya yazarım ve genelde şuna benzer:
158 |
159 | 
160 |
161 | Eğer SplashScreen kullancaksınız bütün asenkron işlemleri yaptıktan sonra Splash ekranını bu component içinde kaldırabilirsiniz.
162 |
163 | Örnek olarak:
164 |
165 | ```tsx filename="./src/bootstrap.tsx"
166 | import React, { useEffect } from 'react';
167 | import SplashScreen from 'react-native-splash-screen';
168 |
169 | import RootStackScreen from './navigation';
170 |
171 | export default function Bootstrap() {
172 | useEffect(() => {
173 | setupApp().then(() => SplashScreen.hide());
174 | }, []);
175 |
176 | return ;
177 | }
178 | ```
179 |
180 | ### Iconlar ve SVG
181 |
182 | Yaptığım uygulamaların hiçbirinde tasarımı ben yapmadım elimde her zaman bir [Figma](https://www.figma.com) dosyası oldu. Figmada
183 | iconları `.svg` olarak export alabildiğimden kolayca React Native içerisinde kullanbiliyordum.
184 |
185 | Bunun için kullandığım araç [SVGR](https://react-svgr.com/). SVGR nin amacı, svg dosyalarını benim için React componentlerine
186 | dönüştürmek.
187 |
188 | #### SVGR kurulumu
189 |
190 | ```shell
191 | $ yarn add @svgr/cli --dev
192 | $ yarn add react-native-svg
193 | ```
194 |
195 | Gerekli kütüphaneleri kurduktan sonra npm scriptleri yardımıyla icon ekleme işlemini otomatikleştiriyorum
196 |
197 | ```json filename="package.json"
198 | "scripts": {
199 | //...
200 | "svgr": "svgr ./src/icons -d ./src/components/icons --native --no-dimensions --typescript --svgo-config .svgorc.json",
201 | }
202 | ```
203 |
204 | Kısaca yaptığı işi açıklamak gerekirse, svgr ye `./src/icon` dizinini okumasını `--native` parametresi ile `react-native-svg`
205 | e uygun işlem yapmasını, `--no-dimesion` ile _width_ ve _height_ değerlerini kaldırmasını, `--typescript` ile Typescript uyumlu
206 | olmasını, `--svgo-config` config dosyasını yolunu ve sonunda oluşturduğu çıktıları `./src/components/icons` dizine
207 | çıkartmasını istediğimizi belirttik. Config dosyasında ise basitçe:
208 |
209 | ```json filename="./scgorc.json"
210 | { "plugins": [{ "removeViewBox": false }] }
211 | // viewboxu kaldırmak için plugin kullandık
212 | ```
213 |
214 | Şimdi scripti çalıştırdığımızda
215 |
216 | ```shell
217 | ❯ yarn svgr
218 | yarn run v1.22.10
219 | $ svgr ./src/icons -d ./src/components/icons --native --no-dimensions --typescript --svgo-config .svgorc.json
220 | src/icons/diamond.svg -> src/components/icons/Diamond.tsx
221 | src/icons/button-refresh.svg -> src/components/icons/ButtonRefresh.tsx
222 | src/icons/arrow-left.svg -> src/components/icons/ArrowLeft.tsx
223 | src/icons/heart.svg -> src/components/icons/Heart.tsx
224 | src/icons/reload.svg -> src/components/icons/Reload.tsx
225 | src/icons/office-file.svg -> src/components/icons/OfficeFile.tsx
226 | src/icons/support-human.svg -> src/components/icons/SupportHuman.tsx
227 |
228 | ✨ Done in 0.98s.
229 | ```
230 |
231 | Daha sonra istediğim iconu herhangi bir yerde kullanmak çok kolay
232 |
233 | ```tsx
234 | import * as Icons from '@/components/icons/';
235 |
236 | ;
237 | ```
238 |
239 | ### Boyutlar, Boşluklar
240 |
241 | Elinizde bir tasarım varsa tasarımda elemente gelip width, height değerlerini pixel cinsiden olduğu gibi
242 | React Native'e vermek çok mantıklı değil. Çünkü faklı ekranlarda aynı pixel değeri istediğimiz sonuçları vermeyebilir.
243 | Bunu çözmek için kullandığım yöntem ise şöyle:
244 |
245 | Diyelim ki referans alıdğınız bir tasarım var
246 |
247 | 
248 |
249 | Görüldüğü üzere tasarımın frame width değeri **375px**. Dolayısıyla ekrana yerlemiş olan elementler de bu genişlik
250 | değeri baz alınarak yapılmış. Biz de tasarımı uygulamaya geçirirken buna benzer bir yöntem kullanacağız.
251 | Bunun için React Native tarafında `PixelRatio` kullanarak ekranın genişliğine göre şöyle bir
252 | _utility_ fonksiyon yazabiliriz
253 |
254 | ```ts filename="./src/utils/index.ts"
255 | import { Dimensions, PixelRatio } from 'react-native';
256 |
257 | let screenWidth = Dimensions.get('window').width;
258 |
259 | // https://www.npmjs.com/package/react-native-responsive-screen
260 | function widthPercentageToDP(widthPercent) {
261 | // Parse string percentage input and convert it to number.
262 | const elemWidth =
263 | typeof widthPercent === 'number' ? widthPercent : parseFloat(widthPercent);
264 |
265 | // Use PixelRatio.roundToNearestPixel method in order to round the layout
266 | // size (dp) to the nearest one that correspons to an integer number of pixels.
267 | return PixelRatio.roundToNearestPixel((screenWidth * elemWidth) / 100);
268 | }
269 |
270 | /**
271 | * @param size size given in the design
272 | * @returns responsive size of the text
273 | */
274 | export function responsiveSize(size: number) {
275 | return widthPercentageToDP((size / 375) * 100);
276 | }
277 | ```
278 |
279 | Daha sonra bu `responsiveSize` methodunu fontlarda, spacing değerlerinde kullanabilirsiniz.
280 |
281 | ```ts filename="./src/style/fonts.ts"
282 | import { responsiveSize as rs } from '@/utils';
283 |
284 | export const size = {
285 | h1: rs(30),
286 | h2: rs(24),
287 | h3: rs(20),
288 | medium: rs(16),
289 | normal: rs(14),
290 | input: rs(16),
291 | regular: rs(14),
292 | small: rs(12),
293 | mini: rs(8),
294 | };
295 | ```
296 |
297 | ### API dizaynı
298 |
299 | Api dizaynı projeden projeye değişebileceği gibi bir REST API ile çalışıyorsanız endpointleri servis dosyaları şeklinde yazmak mantıklı oluyor.
300 |
301 | ```ts filename="/api/HTTPClient.ts"
302 | import Axios from 'axios';
303 |
304 | let baseURL = 'https://api.product.com/';
305 | const TOKEN = '';
306 |
307 | const client = Axios.create({
308 | baseURL,
309 | headers: {
310 | Accept: 'application/json, text/plain, */*',
311 | Authorization: `TOKEN ${TOKEN}`,
312 | },
313 | });
314 |
315 | export default client;
316 | ```
317 |
318 | Diyelim ki API servisiniz size databasede olan araçları veriyor
319 |
320 | ```ts filename="./api/services/Cars.ts"
321 | import HTTPClient from '../HTTPClient';
322 |
323 | export type Car = {
324 | name: string;
325 | model: string;
326 | };
327 |
328 | export async function fetch(limit?: number) {
329 | const { data } = await HTTPClient.get('cars/');
330 | return data;
331 | }
332 | ```
333 |
334 | Bunun gibi bir servis dosyası yazmak, hem düzeni sağlıyor hem de okunabilirliği artırıyor.
335 |
336 | #### Side Effects
337 |
338 | Bir API isteği yapacağımız zaman _loading_, _error_ gibi durumları da
339 | yakalamamız ve kullanıcıya bildirmemiz gerekir.
340 |
341 | Backend servisini basit bir istek yaptığımızı düşünelim. Sayfa açıldığı zaman
342 | verileri çeksin
343 |
344 | ```tsx filename="./views/Cars/index.tsx"
345 | import React, { useEffect, useState } from 'react';
346 | import { View, Text, FlatList } from 'react-native';
347 |
348 | import * as CarService from '@/api/services/Cars';
349 |
350 | export default function Index() {
351 | const [loading, setLoading] = useState(false);
352 | const [error, setError] = useState(null);
353 | const [data, setData] = useState([]);
354 |
355 | useEffect(() => {
356 | setLoading(true);
357 | CarService.fetch()
358 | .then(cars => {
359 | setData(cars);
360 | })
361 | .catch(e => setError(e))
362 | .finally(() => setLoading(false));
363 | }, []);
364 |
365 | return (
366 |
367 | {loading && Loading...}
368 | {error && Error!}
369 | {data && }
370 |
371 | );
372 | }
373 | ```
374 |
375 | Görüldüğü gibi her sayfada en az 3 tane state değişkeni tanımlamamız gerekiyor. Böyle 10 tane sayfanız oluduğunu düşünürseniz sürkeli
376 | bu şekilde tanımlamalar yapmak işkence olacaktır. Bunu daha efektif bir hale getirebiliriz.
377 |
378 | React tarafında backend servisinden veri çekerken [React Query](https://react-query.tanstack.com/) kullanıyorum.
379 | Basit olarak şöyle
380 |
381 | ```tsx filename="./views/Cars/index.tsx"
382 | import React from 'react';
383 | import { View, Text, FlatList } from 'react-native';
384 | import { useQuery } from 'react-query';
385 |
386 | import * as CarService from '@/api/services/Cars';
387 |
388 | export default function Index() {
389 | const { isLoading, isError, data } = useQuery('cars', () =>
390 | CarService.fetch(),
391 | );
392 |
393 | return (
394 |
395 | {isLoading && Loading...}
396 | {isError && Error!}
397 | {data && }
398 |
399 | );
400 | }
401 | ```
402 |
403 | Yazması gerçekten keyifli oluyor tabi. Fakat React Query sadece "kodu kısaltmak" ile kalmıyor.
404 | Caching, Memorizing, Performance Optimization gibi pek çok farklı sorunu çözüyor.
405 |
406 | ### Store Yapısı
407 |
408 | Ben global state için uygulamarımda [Redux](https://redux.js.org/) ve [Redux-Toolkit](https://redux-toolkit.js.org/) kullanıyorum. Reduxdan önce [Mobx](https://mobx.js.org/README.html),
409 | [Mobx-State-Tree](https://mobx-state-tree.js.org/intro/welcome) gibi kütüphaneler de kullandım. Redux o zamanlar bana bir sürü reducer yazdığın,
410 | bir ton _boilerplate_ içeren kod yazmamın gerektiği, kötü bir kütüphane gibi geliyordu. Son zamanlarda
411 | Redux Toolkit ile birlikte Redux yazmak olukça keyifli hale geldi. Redux Toolkit ile
412 | daha az ve anlaşılır kod ile state'i yönetebildiğimi görünce artık projelerimde Redux kullanmaya
413 | başladım.
414 |
415 | Store yazarken dikkat ettiğim 2 şey var. Biri store'u typescript ile doğru ayarlamak.
416 |
417 | ```ts filename="./store/user.slice.ts"
418 | import { createSlice, PayloadAction } from "@reduxjs/toolkit";
419 |
420 | expot type User = {
421 | name: string
422 | }
423 |
424 | interface IUserState {
425 | user: User | null;
426 | }
427 |
428 | const initialState: IUserState = {
429 | user: null
430 | };
431 |
432 | export const userSlice = createSlice({
433 | name: "user",
434 | initialState,
435 | reducers: {
436 | setUser: (state, action: PayloadAction) => {
437 | state.user = action.payload;
438 | },
439 | },
440 | });
441 |
442 | export const { setUser } = appSlice.actions;
443 | export default userSlice.reducer;
444 | ```
445 |
446 | ```ts filename="./store/index.ts"
447 | import { combineReducers, configureStore } from '@reduxjs/toolkit';
448 | import UserSlice from './user.slice';
449 |
450 | const reducers = combineReducers({
451 | user: UserSlice,
452 | });
453 |
454 | const store = configureStore({
455 | reducer: reducers,
456 | });
457 |
458 | export default store;
459 | ```
460 |
461 | Store tipine doğrudan erişmek için `index.d.ts` ye yazmak mantıklı.
462 |
463 | ```ts
464 | import store from '@/store';
465 |
466 | declare global {
467 | // Oher Types...
468 |
469 | // Store Types
470 | type RootState = ReturnType;
471 | type AppDispatch = typeof store.dispatch;
472 | }
473 | ```
474 |
475 | Ardından React uygulamasına özel store için iki basit hook yazabiliriz.
476 |
477 | ```ts
478 | import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
479 |
480 | export const useAppDispatch = () => useDispatch();
481 | export const useAppSelector: TypedUseSelectorHook = useSelector;
482 | ```
483 |
484 | Diğer dikkat ettiğim şey ise. Redux slice'larına özel hooklar yazmak. Mesela yukardaki `user`
485 | için söyle bir hook yazılabilir.
486 |
487 | ```tsx
488 | import { useContext } from "react";
489 |
490 | import { useAppDispatch, useAppSelector, User } from "./useStore";
491 | import { setUser as setUserReducer } from "@/store/user.slice";
492 |
493 | export default function useUser() {
494 | const dispatch = useAppDispatch();
495 |
496 | const user = useAppSelector((state) => state.user);
497 |
498 | const setUser = (user: User) => {
499 | dispatch(setUserReducer(user));
500 | };
501 |
502 | return { user, setUser };
503 | }
504 |
505 |
506 | ...
507 |
508 | // Component içinde
509 | function Index(){
510 | const user = useUser(); // Storedaki user artık burada
511 |
512 | return ...
513 | }
514 |
515 | ```
516 |
517 | Burada ilk kısmı sonlandırıyorum. Buraya kadar hep React, React Native üzerinde durdum. İkinci kısımda (_eğer üşenmeden yazarsam_) build süreçleri, versyonlama,
518 | uygulama içi satın alma ve abonelik gibi konulara gireceğim.
519 |
--------------------------------------------------------------------------------
/pages/posts/building-bulletproof-react-native-applications/provider.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/building-bulletproof-react-native-applications/provider.jpg
--------------------------------------------------------------------------------
/pages/posts/gece-sikilmasi-redux-kaynak-kodundan-ognrediklerim/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Gece sıkılması, Redux kaynak kodundan öğrendiklerim
3 | date: 2022-10-31
4 | tag:
5 | - javascript
6 | - redux
7 | ---
8 |
9 | Redux gercekten hicbir sey yapmiyor.
10 |
11 | Pazar gecesi yaklasik 2 saat [Redux](https://github.com/reduxjs/redux) kaynak kodu okuma
12 | maceramdan cikardigim sonuc bu oldu iste.
13 |
14 | Yani bi'seyler yaptigi kesin, belki "_yaptigi isi cok basit sekilde hallediyor_" da denebilir
15 |
16 | ---
17 |
18 | Her seyden once, Redux nedir?...
19 |
20 | diye baslamayacagim tabi. Buraya kadar geldiyseniz Redux ne biliyorsunuzdur
21 | diye tahmin ediyorum.
22 |
23 | ## Simdi
24 |
25 | Bilginiz uzere `reducer` dedigimiz fonksiyonlar state'i degistirir. Ve bunu
26 | en yalin haliyle size yaptirir. Redux size onceki state'i ve bir action'i
27 | verir. Siz de bu action'a gore yeni state'i dondurursunuz. Bu islemlerin hepsini
28 | siz kendiniz yaparsiniz. Redux sadece uygulamanizdaki bazi yerlere "_state degisti_"
29 | sinyalini gonderir. Peki nasil yapar bunu?
30 |
31 | `createStore` [implementasyonuna](https://github.com/reduxjs/redux/blob/4ebc2bcfb7c8631656ab9dd400c2578afd1b0c41/src/createStore.ts#L42) bu bakalim. Edge caseleri, type checkleri ve
32 | typescript ile ilgili kisimlari gecip asil isleme bakarsak
33 |
34 | [ilgili kisim](https://github.com/reduxjs/redux/blob/4ebc2bcfb7c8631656ab9dd400c2578afd1b0c41/src/createStore.ts#L110-L112)
35 |
36 | ```js
37 | export default function createStore(reducer, preloadedState, enhancer) {
38 | let currentReducer = reducer;
39 | let currentState = preloadedState;
40 | let listeners = [];
41 |
42 | // ...
43 | }
44 | ```
45 |
46 | `reducer` bildidigimiz gibi state'i degistiren fonksiyon. `preloadedState` ise
47 | uygulamanizin baslangic state'i. `enhancer` zimbirtisi middleware
48 | olaylarini sagliyor. Bu yazida onu ele almayacagim.
49 |
50 | `listeners` array'i dikkatimizi cekiyor. Vardir bir hikmeti deyip devam edelim
51 |
52 | kodun ilerleyen kisimlarinda storun kendisini donduren bir `getState` fonksiyonu
53 | var.
54 |
55 | [ilgili kisim](https://github.com/reduxjs/redux/blob/4ebc2bcfb7c8631656ab9dd400c2578afd1b0c41/src/createStore.ts#L134-L144)
56 |
57 | ```js
58 | export default function createStore(/* ... */) {
59 | // ...
60 |
61 | function getState() {
62 | return currentState;
63 | }
64 |
65 | // ...
66 | }
67 | ```
68 |
69 | O anki state'i almamiza yarayan bir fonksiyon. Guzel
70 |
71 | kod `subscribe` methodu ile devam ediyor
72 |
73 | [ilgili kisim](https://github.com/reduxjs/redux/blob/4ebc2bcfb7c8631656ab9dd400c2578afd1b0c41/src/createStore.ts#L190-L209)
74 |
75 | ```js
76 | export default function createStore(/* ... */) {
77 | // ...
78 |
79 | function subscribe(listener) {
80 | listeners.push(listener);
81 | return function unsubscribe() {
82 | const index = listeners.indexOf(listener);
83 | listeners.splice(index, 1);
84 | };
85 | }
86 |
87 | // ...
88 | }
89 | ```
90 |
91 | gorunuse gore bu fonksiyon `listeners` array'ine bi'seyler ekliyor. Ve
92 | `unsubscribe` fonksiyonunu donduruyor. Bu fonksiyonu cagirdigimizda
93 | `listeners` array'inden listener'i cikariyor. Hmm, biraz garip bir islem
94 |
95 | Vakit kaybetmeden devam ediyorum ki buyuk resmi gorelim. Asagida meshur
96 | `dispatch` fonksiyonu var
97 |
98 | [ilgili kisim](https://github.com/reduxjs/redux/blob/4ebc2bcfb7c8631656ab9dd400c2578afd1b0c41/src/createStore.ts#L259-L268)
99 |
100 | ```js
101 | export default function createStore(/* ... */) {
102 | // ...
103 |
104 | function dispatch(action) {
105 | currentState = currentReducer(currentState, action);
106 | listeners.forEach((listener) => listener());
107 | return action;
108 | }
109 |
110 | // ...
111 | }
112 | ```
113 |
114 | Olayin koptugu yer burasi. `dispatch` fonksiyonu bir action aliyor.
115 | Bu action'i `reducer` fonksiyonuna veriyor. `reducer` fonksiyonu yeni state'i
116 | donduruyor. Bu yeni state'i `currentState` degiskenine atiyor. Ve `listeners`
117 | array'indeki tum listenerlar cagiriliyor.
118 |
119 | Buradan cikaracagimiz iki sonuc var. Ilk olarak `dispatch` fonksiyonu `reducer` i
120 | kullanarak state'i degistiriyor ve yeni state olusuyor. Ikinci olarak `listeners` array'i
121 | icinde fonksiyonlar tutuluyor. Bu fonskiyonlarin hepsi `dispatch` calistigi
122 | anda cagiriliyorlar. Bu sayede "state degisti" sinyalini tum
123 | listenerlara (subscribe olmus butun fonskiyonlara) gonderilmis oluyor. Bir onceki adimda
124 | state, `reducer` ile degistigi icin butun listenerlar yeni state ile calisiyor.
125 |
126 | Son olarak gerekli olan degerleri ve fonskiyonlari `createStore` fonksiyonu donduruyor.
127 |
128 | [ilgili kisim](https://github.com/reduxjs/redux/blob/4ebc2bcfb7c8631656ab9dd400c2578afd1b0c41/src/createStore.ts#L359-L368)
129 |
130 | ```js
131 | export default function createStore(/* ... */) {
132 | // ...
133 |
134 | dispatch({ type: '@@redux/INIT' });
135 | return {
136 | dispatch,
137 | subscribe,
138 | getState
139 | };
140 | }
141 | ```
142 |
143 | Goruldugu gibi fonskiyon isini bitirmeden once bir `dispatch` cagiriyor.
144 | Bunun amaci `reducer` fonksiyonu ilk calistiginda baslangic state'i dondurmek.
145 | Boylece verilen `reducer` a gore initial state olusmus oluyor. Burada action
146 | type `@@redux/INIT` olmasi onemli. Cunku `reducer` fonksiyonu bu action
147 | type'i goremezse baslangic state'i dondurmeli.
148 |
149 | Tum kaynak kod sadelestirilmis hali ile su sekilde
150 |
151 | ```js
152 | export default function createStore(reducer, preloadedState, enhancer) {
153 | let currentReducer = reducer;
154 | let currentState = preloadedState;
155 | let listeners = [];
156 |
157 | function getState() {
158 | return currentState;
159 | }
160 |
161 | function subscribe(listener) {
162 | listeners.push(listener);
163 | return function unsubscribe() {
164 | const index = listeners.indexOf(listener);
165 | listeners.splice(index, 1);
166 | };
167 | }
168 |
169 | function dispatch(action) {
170 | currentState = currentReducer(currentState, action);
171 | listeners.forEach((listener) => listener());
172 | return action;
173 | }
174 |
175 | dispatch({ type: '@@redux/INIT' });
176 | return {
177 | dispatch,
178 | subscribe,
179 | getState
180 | };
181 | }
182 | ```
183 |
184 | Bu kadar. Nasil calisitgina bakalim.
185 |
186 | ```js
187 | const counterReducer = (state = 0, action) => {
188 | switch (action.type) {
189 | case 'INCREMENT':
190 | return state + 1;
191 | case 'DECREMENT':
192 | return state - 1;
193 | default:
194 | return state;
195 | }
196 | };
197 |
198 | const store = createStore(counterReducer, 0);
199 |
200 | store.subscribe(() => {
201 | console.log('state changed', store.getState());
202 | });
203 |
204 | store.dispatch({ type: 'INCREMENT' });
205 | // state changed 1
206 | store.dispatch({ type: 'INCREMENT' });
207 | // state changed 2
208 | store.dispatch({ type: 'DECREMENT' });
209 | // state changed 1
210 | ```
211 |
212 | [Kendin dene](https://codesandbox.io/s/distracted-tharp-ykcm55?file=/src/index.js)
213 |
214 | Evet bu kadardi. Redux'un kaynak kodu gercekten cok kisa ama suan NPM de haftalik
215 | indirme sayisi 8 milyon u gecmis durumda. React olmasa bu kadar populer olmazdi fakat
216 | isini guzel yapan bir kutuphane.
217 |
218 | > Ben de basit bir implementasyon yaptim
219 | > [Burada](https://codesandbox.io/s/admiring-mclean-tyfmhk?file=/src/index.js)
220 |
--------------------------------------------------------------------------------
/pages/posts/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | type: posts
3 | title: Posts
4 | ---
5 |
6 | # Posts
7 |
--------------------------------------------------------------------------------
/pages/posts/python-dataclasses/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: Python Dataclasses
3 | date: 2020-11-01
4 | tags:
5 | - python
6 | ---
7 |
8 | ## Nedir?
9 |
10 | Python 3.7 ile gelen güzel bir yapı olan `dataclass`'lar, Python geliştiricileri için mükemmel bir yenilik. Bu yazıda elimden geldiğince bu yapıyı anlatmaya çalısacağım.
11 |
12 | Efendim `dataclass`'lar normal class'lardaki kendini sürekli tekrarlayan kodları yazmayı engelleyen, içinde (genelde) veri depolayan classlardır. Aşağıdaki örnekte bir `dataclass`'ın nasıl tanımladığını görebilirsiniz.
13 |
14 | > Unutmayın `dataclass`'lar Python'un 3.7 sürümünde bulunur.
15 |
16 | ```python
17 | from dataclasses import dataclass
18 |
19 | @dataclass
20 | class Person:
21 | name: str
22 | age: int
23 | ```
24 |
25 | Görüldüğü gibi `dataclass`'lar `@dataclass` decoratoru ile tanımlanır.
26 |
27 | > Sınıf değişkenlerinin yanında tipinin yazıldığını fark etmişsinizdir. Bu Python'da [Type Annotations](https://docs.python.org/3/library/typing.html) olarak geçer. `dataclass`'lar bunu kullandığından dolayı bilmek önemlidir. [Şurada](https://realpython.com/python-type-checking/) type annotations ile ilgili cok güzel bir makale var.
28 |
29 | Bir `dataclass`, bazı [special method](https://docs.python.org/3/reference/datamodel.html#special-method-names)'ları sizin için değiştirir. Mesela `__str__()` methodu otomatik olarak sınıf içindeki değerleri bastıracaktır.
30 |
31 | ```python
32 | >>> p = Person("Ömer", 19)
33 | >>> p
34 | Person(name='Ömer', age=19)
35 | >>> p.name = "Ahmet"
36 | >>> p.age = 30
37 | >>> p
38 | Person(name='Ahmet', age=30)
39 | ```
40 |
41 | Eğer biz böyle bir çıktı isteseydik şöyle bir şey yazmamız gerekirdi;
42 |
43 | ```python
44 | class NPerson:
45 | def __init__(self, name, age):
46 | self.name = name
47 | self.age = age
48 |
49 | def __repr__(self):
50 | return f"{self.__class__.__name__}(name={self.name!r}, age={self.age})"
51 |
52 | def __str__(self):
53 | return self.__repr__()
54 | ```
55 |
56 | Görüldüğü gibi bir değişkeni `__init__`'den alıp nesne değişkeni yapmak için 3 kere yazmamız gerekiyor.
57 |
58 | Ayrıca dikkatli bakarsanız objelerin tam olarak _tanımlanmadığını_ göreceksiniz.
59 |
60 | ```python
61 | >>> p = Person("Ömer", 19)
62 | >>> p == Person("Ömer", 19)
63 | True
64 | >>> np = NPerson("Ahmet", 30)
65 | >>> np == NPerson("Ahmet", 30)
66 | False
67 | ```
68 |
69 | Bunun nedeni `dataclass`'ların `__eq__` methodunu override etmiş olmaları. Normalde Python, obje karşılaştırma yaparken objelerin adreslerini karşılaştırır ama `dataclass`'lar sınıf içindeki değerleri karşılaştırır. Eğer bunu kendiniz yazmak isteseydiniz;
70 |
71 | ```python
72 | class NPerson:
73 | [...]
74 | def __eq__(self, other):
75 | if other.__class__ is not self.__class__:
76 | return NotImplemented
77 | return (self.name, self.age) == (other.name, other.age)
78 | ```
79 |
80 | `dataclass`'lar bunu bizim için yapar.
81 |
82 | `dataclass`'lara default değerler de verebiliriz;
83 |
84 | ```python
85 | @dataclass
86 | class Person:
87 | name: str
88 | age: int = 18
89 |
90 | >>> p = Person("Ahmet")
91 | >>> p
92 | Person(name='Ahmet', age=18)
93 | ```
94 |
95 | > **Not:** `dataclass`'lar (ve Python) aslında değişkenlerin tipine dikkat etmez. Type annotations sadece okunabilirliği artırır.
96 |
97 | ```python
98 | from dataclasses import dataclass
99 | from typing import Any
100 |
101 | @dataclass
102 | class Person:
103 | name: Any
104 | age: str
105 |
106 | >>> p = Person(14, "Foo")
107 | >>> p
108 | Person(name=14, age='Foo')
109 | ```
110 |
111 | Şu ana kadar hiç fonksiyon yazmadık ama `dataclass`'da fonksiyon yazmak ile normalde yazmak arasında hiç fark yoktur.
112 |
113 | ```python
114 | from dataclasses import dataclass
115 | from typing import List
116 |
117 | @dataclass
118 | class Student:
119 | name: str
120 | age: int
121 | results: List[int]
122 |
123 | def average(self):
124 | return sum(self.results) / len(self.results)
125 | ```
126 |
127 | ```python
128 | >>> st = Student("Ömer", 19, [85,97,67])
129 | >>> st.average()
130 | 83.0
131 | ```
132 |
133 | Biraz `@dataclass` decoratoru hakkında konuşalım. `@dataclass`decoratoru birçok parametre alabilir.
134 |
135 | ```python
136 | @dataclass
137 | class Foo:
138 | [...]
139 |
140 | # Aslında buna eşittir.
141 |
142 | @dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False)
143 | class Foo:
144 | [...]
145 |
146 | ```
147 |
148 | - `init`: eğer `True` ise `__init__` fonksiyonunu override eder.
149 |
150 | - `repr`: eğer `True` ise `__repr__` fonksiyonunu override eder.
151 |
152 | - `eq`: eğer `True` ise `__eq__` fonksiyonunu override eder. Bu konuya yukarda değinmiştik.
153 |
154 | - `order`: eğer `True` ise (varsayılan `False`) `__lt__`, `__le__`, `__gt__` ve `__ge__` fonksiyları override eder. Bu fonksiyonlar karşılaştırma fonksiyonlarıdır. `dataclass`, `__eq__`'de olduğu gibi class değerlerini karşılaştırır.
155 |
156 | - `frozen`: eğer `True` ise nesne oluşturduktan sonra gelen değer atamaları `FrozenInstanceError` hatasını raise edecektir.
157 |
158 | ```python
159 | from dataclasses import dataclass
160 | from typing import List
161 |
162 | @dataclass(frozen=True)
163 | class Student:
164 | name: str
165 | age: int
166 | results: List[int]
167 |
168 | def average(self):
169 | return sum(self.results) / len(self.results)
170 | ```
171 |
172 | ```python
173 | >>> st = Student("Ömer", 19, [85,97,67])
174 | >>> st.name = "Ahmet"
175 | ---------------------------------------------------------------------------
176 | FrozenInstanceError Traceback (most recent call last)
177 | in
178 | ----> 1 st.name = "Ahmet"
179 |
180 | in __setattr__(self, name, value)
181 |
182 | FrozenInstanceError: cannot assign to field 'name'
183 | ```
184 |
185 | `dataclass` ile kalıtım da yapabiliriz.
186 |
187 | ```python
188 | from dataclasses import dataclass
189 | from typing import List
190 |
191 | @dataclass
192 | class Person:
193 | name: str
194 | age: int
195 |
196 | def say(self, s):
197 | print(f"{self.name}: {s}")
198 |
199 | @dataclass
200 | class Student(Person):
201 | results: List[int]
202 |
203 | def average(self):
204 | return sum(self.results) / len(self.results)
205 | ```
206 |
207 | ```python
208 | >>> st = Student("Ömer", 19, [85,97,67])
209 | >>> st.say("Hello world")
210 | Ömer: Hello world
211 | ```
212 |
213 | ## Alternatifler
214 |
215 | `dataclass`'ların (genellikle) veri depoladığını söylemiştik. Bunu Python'da sadece `dataclass`'ların yapmadığını görmüşsünüzdür. Basit veri yapıları olan `tuple` ve `dict` de veri depolar.
216 |
217 | ```python
218 | person_tuple = (19, "Ömer") # Tuple
219 | person_dict = {'age': 19, 'name': 'Ömer'} # Dict
220 | ```
221 |
222 | Ama dikkat ederseniz `dataclass`'lar kadar _kullanışlı_ olmadığını görürsünüz. Mesela `tuple`'de argumanların yerlerini karıştırabilirsiniz debug ederken bu işinizi çok zorlaştır. `dict` de ise dataya erişmek için mutlaka bir `key`'e ihtiyaç vardır.
223 |
224 | ```python
225 | person_dict['name'] ## person_dict.name desek daha hoş olmaz mı?
226 | ```
227 |
228 | > Aslında `dict` veri tipindeki objelerin verilerine nokta(.) notasyonu ile erişebiliriz. Şöyle:
229 |
230 | > Konu ile ilgili içerik [Sözlük Veri Tipini Python Nesnesine Dönüştürme](https://www.coogger.com/@hakancelik96/sozluk-veri-tipini-python-nesnesine-donusturme/)
231 |
232 | ```python
233 | class NDict(dict):
234 | def __init__(self, *arg, **kwargs):
235 | for key, value in kwargs.items():
236 | setattr(self, key, value)
237 | super().__init__(*arg, **kwargs)
238 |
239 | person = NDict(name="Ömer", age=19)
240 | person.age # 19
241 | person.name # Ömer
242 | ```
243 |
244 | Tabi bunun ne kadar zahmetli olduğunu görüyorsunuz. Ama durun yukarıdaki kodun daha iyisini yapan bir veri yapısı var zaten. `namedtuple`
245 |
246 | ```python
247 | from collections import namedtuple
248 |
249 | Person = namedtuple("Person", ['age', 'name'])
250 | person = Person(16, "Ömer")
251 |
252 | person
253 | # Person(age=16, name='Ömer')
254 | person.name
255 | # Ömer
256 | ```
257 |
258 | E `dataclass`'lardan farkı ne bunun?
259 | Öncelikle `dataclass`'ların çok daha fazla özelliği buluyor. Yukarıda anlattığım _kalıtım_ ve _fonksiyon_ ekleme işlemleri `namedtuple`'de çok daha zor. Öte yandan karşılaştırma yaparken `namedtuple` istediğinizi vermeyecektir. Yukarıdaki örnekten devam edelim
260 |
261 | ```python
262 | >>> person == (16, "Ömer")
263 | True
264 | ```
265 |
266 | İyi bir şey gibi gözükse de sonuçta kendi türünde olmadığını _zannettiğimiz_ objelerle tam anlamıyla doğru karşılaştırmalar vermiyor.
267 |
268 | Ayırca `namedtuple` obje oluştuktan sonra verilerin değişmesine izin vermeyecektir.
269 |
270 | ```python
271 | Person = namedtuple("Person", ['age', 'name'])
272 | person = Person(16, "Ömer")
273 | person.age = 22
274 | ----------------------------------------
275 | AttributeError Traceback (most recent call last)
276 | in
277 | 3 Person = namedtuple("Person", ['age', 'name'])
278 | 4 person = Person(16, "Ömer")
279 | ----> 5 person.age = 22
280 |
281 | AttributeError: can't set attribute
282 | ```
283 |
284 | ## field()
285 |
286 | Bir seneryo üzerinden devam edelim.
287 |
288 | ```python
289 | from dataclasses import dataclass
290 | from typing import List
291 |
292 | @dataclass
293 | class Student:
294 | id: int
295 | name: str
296 |
297 | @dataclass
298 | class Lesson:
299 | students: List[Student]
300 | ```
301 |
302 | Buradan yeni nesneler üretelim
303 |
304 | ```python
305 | omer = Student(1, "Ömer")
306 | bersu = Student(2, "Bersu")
307 | math = Lesson([omer, bersu])
308 | print(math)
309 | # Lesson(students=[Student(id=1, name='Ömer'), Student(id=2, name='Bersu')])
310 | ```
311 |
312 | Şimdi `Lesson` sınıfına default deger vermeyi deneyelim. Bunu yaparken bir factory fonksiyon yazalım.
313 |
314 | ```python
315 | NAMES = ["Ömer", "Ahmet", "Cem", "Zehra", "Büşra", "Bersu"]
316 |
317 | def collect_students():
318 | return [Student(i + 1, v) for i, v in enumerate(NAMES)]
319 |
320 | collect_students()
321 |
322 | # [Student(id=1, name='Ömer'),
323 | # Student(id=2, name='Ahmet'),
324 | # Student(id=3, name='Cem'),
325 | # Student(id=4, name='Zehra'),
326 | # Student(id=5, name='Büşra'),
327 | # Student(id=6, name='Bersu')]
328 | ```
329 |
330 | Teoride `Lesson`a varsayılan değer vermek için şöyle yaparsınız.
331 |
332 | ```python
333 | @dataclass
334 | class Lesson:
335 | students: List[Student] = collect_students()
336 | ```
337 |
338 | Böyle bir tanım Python'ın en büyük anti-pattern'lerinden birisidir: Varsayılan olarak değişken değer kullanmak. Buradaki problem şu ki `Lesson`'nun tüm versiyonları aynı `.students`'in varsayılan liste objesini kullanacak. Kısacası bir `Lesson`'dan herhangi bir `Student` silindiği vakit `Lesson`'nun tüm versiyonlarından da silinecek. Aslına bakarsanız dataclass'lar bunun olmasının önüne geçip size ValueError döndürüyor.
339 |
340 | ```python
341 | ---------------------------------------------------
342 | ValueError Traceback (most recent call last)
343 | in
344 | 12 name: str
345 | 13
346 | ---> 14 @dataclass
347 | 15 class Lesson:
348 | 16 students: List[Student] = collect_students()
349 |
350 | \python\python37-32\lib\dataclasses.py in _get_field(cls, a_name, a_type)
351 | 725 # For real fields, disallow mutable defaults for known types.
352 | 726 if f._field_type is _FIELD and isinstance(f.default, (list, dict, set)):
353 | --> 727 raise ValueError(f'mutable default {type(f.default)} for field '
354 | 728 f'{f.name} is not allowed: use default_factory')
355 | 729
356 |
357 | ValueError: mutable default for field students is not allowed: use default_factory
358 | ```
359 |
360 | Bunun önüne geçmek için `field` methodunun `default_factory` diye bir parametresi var.
361 |
362 | ```python
363 | from datacasses import dataclass, field
364 |
365 | @dataclass
366 | class Lesson:
367 | students: List[Student] = field(default_factory=collect_students)
368 |
369 | Lesson()
370 | # Lesson(students=[Student(id=1, name='Ömer'), Student(id=2, name='Ahmet'), Student(id=3, name='Cem'), Student(id=4, name='Zehra'), Student(id=5, name='Büşra'), Student(id=6, name='Bersu')])
371 | ```
372 |
373 | `field`, sadece `default_factory` ile sınırlı değil. [Bu bağlantıdan](https://docs.python.org/3/library/dataclasses.html#dataclasses.field) diğer parametrelere ve ne işe yaradıklarına ulaşabilirsiniz.
374 |
375 | ## Optimizasyon
376 |
377 | Bahsedeceğim şey `__slots__`, `__slots__` kısaca, sınıflara dinamik olmayan _sabit_ attributelar belirleyerek RAM'dan ve _hızdan_ tasarruf sağlıyor. `__slots__`, kendi başına ele alınması gereken bir konu oluğu için [şuradan](https://medium.com/@mazlumagar/python-tricks-1-slots-e0c9b04f4c5a) daha fazla bilgiye ulaşabilirsiniz.
378 |
379 | `dataclass`'larda `__slots__` kullanımı normal classlardaki gibidir.
380 |
381 | ```python
382 | from dataclasses import dataclass, field
383 |
384 | @dataclass
385 | class NormalPerson:
386 | name: str
387 | age: int
388 | salary: int
389 |
390 | @dataclass
391 | class SlotPerson:
392 | __slots__ = ['name', 'age', 'salary']
393 | name: str
394 | age: int
395 | salary: int
396 | ```
397 |
398 | Hafızada sahip olduğu büyüklüğe bakalım.
399 |
400 | ```python
401 | from sys import getsizeof
402 |
403 | getsizeof(NormalPerson("Ahmet", 33, 3000)), getsizeof(SlotPerson("Ahmet", 33, 3000))
404 | # (32, 36)
405 | ```
406 |
407 | Ayırca Python'un veriye erişmesi de normal class'lara göre daha hızlıdır.
408 |
409 | ```python
410 | from timeit import timeit
411 |
412 | timeit(setup="slot_p = SlotPerson('Ahmet', 33, 3000)", globals=globals())
413 | # 0.012656699999752163
414 | timeit(setup="normal_p = NormalPerson('Ahmet', 33, 3000)", globals=globals())
415 | # 0.012095599999156548
416 | ```
417 |
418 | tabi yazmış olduğumuz sınıfın basitliğinden dolayı aradaki fark oldukça az. Daha büyük sınıflarda bu fark dikkate değer biçimde artıyor.
419 |
--------------------------------------------------------------------------------
/pages/posts/use-native-driver/Fixed.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/use-native-driver/Fixed.gif
--------------------------------------------------------------------------------
/pages/posts/use-native-driver/Panic.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/use-native-driver/Panic.gif
--------------------------------------------------------------------------------
/pages/posts/use-native-driver/SmoothAnimation.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bufgix/website/6a87825ac1d187f85e0b90ee198e48c28182b667/pages/posts/use-native-driver/SmoothAnimation.gif
--------------------------------------------------------------------------------
/pages/posts/use-native-driver/index.mdx:
--------------------------------------------------------------------------------
1 | ---
2 | title: useNativeDriver üzerine
3 | date: 2021-11-01
4 | tag:
5 | - react-native
6 | - animation
7 | ---
8 |
9 | Eğer Raact Native'de animasyon işleriyle uğraştıysanız, animasyonu çalıştırırken `useNativeDriver` parametresini görmüşsünüzdür. Bu paramereyi ilk gördüğümde düşündüğüm şey, "Sanırım animasyon native kısımda çalışacak" oldu. Evet gerçekten de `useNativeDriver` animasyonu native tarafda çalıştırıyor. Peki bunu _nasıl_ yapıyor?
10 |
11 | Her şeyden önce bir şeyi açıklığa kavuşturalım. React Native, bütün ekranları native olarak oluşturur yani animasyonların da native olarak oluşturulması gerekir. Javascript bir kutunun yerinin değiştirirken değişikliği haber vermek için bir şekilde işletim sistemi ile haberleşmesi gerekir. Javascript ile Native kod farklı threadlarda çalıştığı için (UI Thread) değişikliğin React Native'in sağladığı "**bridge**" ile işletim sistemine aktarılması gerekir.
12 |
13 | ### Animasyonları JS'ye vermek
14 |
15 | Bir animasyon javascript tarafında kabaca şu adımları takip ederek oluşturulur.
16 |
17 | - Animasyon başlar
18 | - JS `requestAnimationFrame` fonksiyonunu çalıştırır. (Bu fonksiyon her saniye 60 kere çalıştırılmaya çalışılır.)
19 | - JS değiştirilmek istenen şeyin bir sonraki konumunu/opaklığını/transformunu hesaplar
20 | - JS hesapladığı değişikliği `bridge` aracılığıyla işletim sistemine gönderir.
21 | - Köprünün diğer ucunda bulunan native kod, (Android için Java, IOS için Obj-C) belirtilen transform işlemini uygular.
22 | - Ekranda yeni değişiklik güncellenir.
23 |
24 | Burada dikkat etmeniz gereken nokta şudur; Animasyon devam ederken React _re-render_ işlemi yapmaz. Bunun sebebi `Animated API`' nin değeri _doğrudan_ native tarafa göndermesidir. Eğer öyle olmasaydı, animasyon değeri bir _React state'i_ gibi olsaydı, 60 saniyede bir component render olacaktı. Bu çok ciddi bir *performans kaybı *olurdu.
25 |
26 | Her şey güzel JS animasyonlarımızı güzel güzel oluşturuyor. Fakat...
27 |
28 | JS **tek thread**'da çalışır yani animasyonun bir sonraki değeri hesaplanırken aynı anda başka bir işlem **yapamayız**. Bu da JS tarafında oluşturulan animasyonun "gecikmeli(_laggy_)" olmasına sebep olur.
29 |
30 | Bir örnek bu gecikmeyi daha iyi anlamanıza yardımcı olabilir.
31 |
32 | ```jsx {11}
33 | import React, { useRef } from 'react';
34 | import { SafeAreaView, Animated, TouchableOpacity, Text } from 'react-native';
35 |
36 | const App = () => {
37 | const scale = useRef(new Animated.Value(1)).current;
38 |
39 | const startAnimation = () => {
40 | Animated.timing(scale, {
41 | toValue: 2,
42 | duration: 600,
43 | useNativeDriver: false // Native dirver devre dışı
44 | }).start();
45 | };
46 |
47 | return (
48 |
49 |
58 |
59 | Press Me
60 |
61 |
62 | );
63 | };
64 |
65 | export default App;
66 | ```
67 |
68 | 
69 |
70 | Evet JS güzel bir şekilde animasyonu çalıştırıyor. Biraz daha zorlayalım
71 |
72 | ```js {8-13}
73 | const startAnimation = () => {
74 | Animated.timing(scale, {
75 | toValue: 2,
76 | duration: 600,
77 | useNativeDriver: false,
78 | }).start();
79 |
80 | setTimeout(() => {
81 | let r = 0
82 | for(let i = 0; i < 1000000000; i++) {
83 | r = i ** i
84 | }
85 | }, 10)
86 | ```
87 |
88 | 
89 |
90 | Görüldüğü üzere JS threadinda FPS düştü ve animasyon* akıcılığını kaybetti.* Bunun nedeni daha önce dediğimiz gibi `scale` değerinin diğer kodlarla birlikte JS threadinda çalışması ve diğer kodları beklemesi.
91 |
92 | Biraz daha açarsak, animasyon başladığı anda `requestAnimationFrame`saniyede 60 kere çalıştırılmaya çalışılacak. JS her defasında bir işlemi yapabileceği için
93 |
94 | ```js
95 | setTimeout(() => {
96 | let r = 0;
97 | for (let i = 0; i < 1000000000; i++) {
98 | r = i ** i;
99 | }
100 | }, 10);
101 | ```
102 |
103 | bu kod JS threadi _meşgul_ edecek bir bir sonraki animasyon karesi atlanacak. Sonuç olarak "smooth" olmayan animsayonlar oluşacak.
104 |
105 | Neyse ki React Native, native tarafta animasyonları oluşturmamıza izin verir.
106 |
107 | ### Animasyonları native tarafta çalıştırmak
108 |
109 | Yukardaki problemi basitçe `useNativeDriver` parametresini `true` yaparak çözebilirsiniz
110 |
111 | ```js {4}
112 | Animated.timing(scale, {
113 | toValue: 2,
114 | duration: 600,
115 | useNativeDriver: true
116 | }).start();
117 | ```
118 |
119 | 
120 |
121 | Peki bu nasıl oluyor? Kabaca şöyle;
122 |
123 | - Animasyon başlar
124 | - JS animasyon için gerekli olan önbilgiyi "bridge" aracılığıyla aktarır
125 | - **Native tarafta** animasyon oluşturulur.
126 |
127 | Bu şekilde animasyon hesaplamalarını JS threadinden ayırdık. Böylelikle hem daha **hızlı animasyonlar** yaptık hem de React Native Bridge'i _darboğaz_ yapmamış olduk.
128 |
129 | Bu noktada aklınıza şu gelebilir; "E o zaman `useNativeDriver`'i her zaman açalım"
130 |
131 | Animasyonları native tarafta çalıştırmanın bazi **eksileri** vardır.
132 |
133 | - Animasyonlar üstünde daha az yetkiye sahip olursunuz. (JS, animasyon devam ederken olan biteni gözlemleyemez.)
134 | - Daha az özeliği manipüle edebilirsiniz. Mesela `height` veya `width` özeliklerini native olarak animate edemezsiniz.
135 |
136 | ### Sonuç olarak
137 |
138 | Animasyonların React Native'de nasıl çalıştığını, hangi aşamalardan geçtiğini, `useNativeDriver`'in ne olduğunu, ne işe yaradığını göstermeye çalıştım. Umarım paylaştığım bilgiler yararlı olur.
139 |
--------------------------------------------------------------------------------
/pages/tags/[tag].mdx:
--------------------------------------------------------------------------------
1 | ---
2 | type: tag
3 | title: Tagged Posts
4 | ---
5 |
6 | import { useRouter } from 'next/router';
7 |
8 | export const TagName = () => {
9 | const { tag } = useRouter().query;
10 | return tag || null;
11 | };
12 |
13 | # Posts Tagged with “”
14 |
--------------------------------------------------------------------------------
/theme.config.jsx:
--------------------------------------------------------------------------------
1 | const currentYear = new Date().getFullYear();
2 |
3 | export default {
4 | footer: {currentYear} © Faruk
,
5 | head: ({ meta }) => (
6 | <>
7 | {meta.description && (
8 |
9 | )}
10 | {meta.tag && }
11 | {meta.author && }
12 | >
13 | ),
14 | readMore: 'Read More →',
15 | postFooter: null,
16 | darkMode: true,
17 | navs: [],
18 | };
19 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/tsconfig",
3 | "compilerOptions": {
4 | "target": "ES2022",
5 | "lib": [
6 | "dom",
7 | "dom.iterable",
8 | "ES2022"
9 | ],
10 | "allowJs": true,
11 | "skipLibCheck": true,
12 | "strict": true,
13 | "noEmit": false,
14 | "esModuleInterop": true,
15 | "module": "esnext",
16 | "moduleResolution": "Bundler",
17 | "resolveJsonModule": true,
18 | "isolatedModules": true,
19 | "moduleDetection": "force",
20 | "incremental": true,
21 | "noUncheckedIndexedAccess": true,
22 | "jsx": "preserve"
23 | },
24 | "exclude": [
25 | "node_modules",
26 | "build",
27 | "dist",
28 | ".next",
29 | ".expo"
30 | ],
31 | "include": [
32 | "next-env.d.ts",
33 | "**/*.ts",
34 | "**/*.tsx"
35 | ]
36 | }
37 |
--------------------------------------------------------------------------------