•
여러 아이템들 중 보이는 것들만 Rendering 해두는 것은 굉장히 중요하며, 그 보이는 것들 중에서도 모든 항목들이 fetch 될 때까지 기다렸다가Rendering하는 것은 좋은 방법은 아니다. 이런 것들이 중요한 이유는 Render Timing을 줄여야 하는 것에 있다.
•
즉, 우리가 원하는 것은 보이는 것들만 Fetching하는 것과 각 아이템들이 Fetching되는 즉시 Rendering되는 것이다. 이는 Bloc 작성 시, StreamBuilder를 이용하여 받은 Stream을 통해 Future<Item>을 return하는 FutureBuilder을 이용하고 ListView.builder를 써야 한다.
•
StreamController와 유사한 역할을 하는 rxdart는 PublishSubject이다. StreamController는 sink와 stream을 모두 관리한다.
•
x라는 스크립트에 y라는 스크립트를 import 한 후 export y를 하면, x를 import하는 다른 스크립트에서 y를 import하지 않아도 y를 이용할 수 있다.
•
bloc의 데이터가 바뀌면 re-render되기 때문에, build function에 bloc의 데이터가 바뀌는 것들을 두면 무한히 데이터 변경 → build → 데이터 변경 → build 무한 루프에 빠질 수 있으므로 주의해야 한다.
•
또한, streambuilder로 listview 구성 시에 각 아이템 역시 streambuilder로 futurebuilder 구성 후 위젯을 만든다고 할 때, listview에 뿌려지는 stream이 순서대로 각 streambuilder를 거쳐서 자기 position이 맞는지 확인하게 된다. 이 과정에서 자기 자신이 아닌 곳에는 initial state를 두기 때문에, 정상적으로 id를 잘 뿌린 것 같아도 rendering이 안 되는 이슈가 있을 수 있다. (Stateless기 때문에 build 한 위젯은 자신의 state를 알고 있을 수 없다는 것이다.)
•
그렇다면 위의 이슈는 어떻게 고치면 좋을까? → ScanStreamTransformer (Stream으로 출력된 data가 StreamBuilder의 FutureBuilder로 들어가기 전에 transformer을 통해서 해결하면 된다.)
◦
이 transformer에 거칠 때 cache map에 등록하게 한다. 그리고 StreamBuilder가 위젯을 생성할 때 해당 cache map을 조회하여 자신이 build할 data가 있는지, 올바른지 확인하게 되는 것이다.
◦
ScanStreamTransformer의 callback function 첫 인자인 cache는 ScanStreamTransformer의 두번째 인자의 map을 참조하는 인자이다.
◦
callback function 두 번재 인자는 비교 필드의 값으로 들어갈 인자이다.
◦
세번째 인자는 iterating의 index로 사용할 인덱스 인자이다.
•
하지만 구현된 transformer를 stream.transform으로 이용하여 stream을 받아오려 할 때 주의할 점은 transform function에 들어간 StreamTransformer가 새로운 instance로 계속 생성되면 separate된 새 instance의 cache에서 비교 작업을 하게 되므로 정상 작동하지 않는다는 점이다. 이는 Single Transformer로 적용할 필요 있다. 또한 이는 Bloc의 Constructor을 통해서 해결함 (단, stream은 고정으로 둬야 한다.)
•
Stream을 이용할 때는 항상 Subscription을 잘 고려하여 이용하여야 한다... 그렇지 않으면 무한적으로 data fetching & rendering이 일어나면서 이상한 state에 빠질 수 있다.
◦
예제의 경우 item이라는 Stream Transformer가 담긴 Stream에서 event를 넘겨주게 되면 각 항목들이 event를 받은 뒤 event가 다시 subscription을 타고 stream에 반영되는 루프 구조이다.
◦
즉, stream이 transformer를 거쳐 subscription을 판별 한 후, Widget에 반영 되면 안 된다.
◦
stream이 각 subscription에 대해 먼저 판별 한 후, transformer을 거쳐 Widget에 반영되는 구조가 되어야 한다.
◦
따라서 item Fetch를 해오는 부분을 따로 두어야 한다. (another stream)
◦
두개의 stream을 묶어서 구현하고자 할 때, 한 stream의 output을 다른 stream의 input으로 넘기고 싶다면, stream.transform.pipe()가 필요하다.
•
StreamBuilder의 Rendering 이슈를 해결 했다면, database conflict에 대해서도 해결해야 한다.
◦
State를 읽을 때마다 기존 database에 저장한 내용들이 충돌난다.
◦
db에 insert 코드에 특별한 처리가 필요하다. (두 가지 방법 있다.)
1.
conflictAlgorithm 이라는 옵션을 생성한다.
2.
insert 구문에 직접 관여하는 로직을 구현하다.
•
null check는 중요함 ?? 기호를 적극 활용한다.
•
Rendering할 때 현재 보이는 것들만 Rendering하는 것도 중요하지만, 모든 데이터들이 다 load 되어야 Render되는 현상도 해결이 필요하다. 이는 현재 보이는 화면의 데이터들만 Fetching하게 해서 이용하는 것이 필요하다.
◦
해결 방법은 간단하다.
◦
ListView 자체가 보이는 것들만 Rendering하기 때문에, ListView안의 요소들을 정적인 높이 값을 할당하게 하면 얼마나 Render해야하고, 얼마나 Fetch 해야 하는지 알 수 있다.
◦
따라서 Stream이 받아지기 전 높이 값이 명확한 Loading으로 쓸 Widget을 먼저 할당하여 자리를 먼저 잡은 후 Stream을 받아서 Widget으로 Rendering하면 주어진 화면 크기에 맞는 개수의 Widget들만 Render & Fetch가 가능하다.
•
Stream 받아는 왔는데, 해당 Stream의 데이터가 업데이트 되었을 때 볼 수 있는 것도 중요하다.
◦
Refresh Indicator가 필요하다.
◦
Bloc 로직이 꼬이지 않도록 하는 것이 중요하다.
◦
Refresh 시 새로운 데이터를 받아오게 되므로, 기존의 cache는 날려버리고 다시 cache를 저장하는 것이 좋다.
** data flow의 변화를 감지하여 작업? >> stream이 필요하다.
•
await for와 snapshot을 이용하여 가능하지만 widget을 이용해도 된다.
•
async snapshot은 query snapshot을 포괄하는 개념이다.
** Private 속성 부여 하는 방식 및 get을 이용하는 데에는 class의 멤버 변수에 대한 직접적인 수정을 막기 위한 것인데, get을 그대로 쓰면 어쨌든 직접 접근이 가능하므로 바뀌지 않는 data type을 주는 것이 더 좋음 따라서 UnmodifiableListView를 부여한다. (return type을 맞춰줘야 한다.)