Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reusability breaks view updates #301

Open
Samigos opened this issue Sep 27, 2022 · 0 comments
Open

Reusability breaks view updates #301

Samigos opened this issue Sep 27, 2022 · 0 comments

Comments

@Samigos
Copy link

Samigos commented Sep 27, 2022

Hi everyone! I've recently started using this pager (2.5.0) on a production app and I've been facing a recycling issue I’ve not been able to solve.

Each page has 1 post, that fills the whole screen. The first batch of posts is fetched and displayed with no issues. After that, I dynamically add new posts at the top of the list; 1 post at a time. That’s when the issue arises! It seems that the new post view copies the state of the next and shows a subview that shouldn’t show.

Here's the pager:

Pager(page: viewModel.feedPage,
                      data: viewModel.feedItems,
                      id: \.self,
                      content: { index in
                        if index < viewModel.posts.count {
                            PostView(post: viewModel.posts[index])
                        } else {
                            Text("Loading...")
                                    .foregroundColor(.white)
                                    .font(.default(size: 17))
                        }
                })
                .vertical()
                .sensitivity(.custom(0.1))
                .onPageWillChange { index in
                    viewModel.willChangeVerticalPage(index: index)
                }
struct PostView: View {
    @StateObject private var viewModel = ViewModel()
    let post: Post
    
    var body: some View {
        VStack {
            SendFriendRequestView(userId: post.user.id) // the subview inside UserViews is the issue
            PostImageView(imageURL: post.imageURL, text: post.text)
        }
        .onAppear {
            viewModel.setUp(post: post)
        }
        .environmentObject(viewModel)
    }
}
fileprivate struct SendFriendRequestView: View {
    @EnvironmentObject private var viewModel: PostView.ViewModel
    let userId: String
    
    var body: some View {
        Button {
            viewModel.sendFriendRequest(userId: userId)
        } label: {
            HStack {
                switch viewModel.sendFriendRequestViewState {
                case .send: sendView
                case .loading: loadingView
                case .waiting: waitingView
                case .hidden: EmptyView()
                }
            }
        }
        .disabled(viewModel.sendFriendRequestViewState != .send)
    }
    
    var sendView: some View {
        HStack {
            Image(systemName: "person.2.fill")
                .resizable()
                .scaledToFit()
                .foregroundColor(.white)
                .frame(width: 10, height: 10)
            
            Text("Add+")
                .foregroundColor(.white)
                .font(.default(size: 13))
        }
        .padding(8)
        .background(
            Capsule().fill(.white.opacity(0.2))
        )
    }
    
    var loadingView: some View {
        ProgressView()
            .tint(.white)
            .padding(8)
            .background(
                Capsule().fill(.white.opacity(0.2))
            )
    }
    
    var waitingView: some View {
        HStack {
            Text("Sent")
                .foregroundColor(.white)
                .font(.default(size: 13))
            
            Image(systemName: "checkmark")
                .resizable()
                .scaledToFit()
                .foregroundColor(.white)
                .frame(width: 10, height: 10)
        }
        .padding(8)
        .background(
            Capsule().fill(.white.opacity(0.2))
        )
    }
}

And here are the involved pieces of the view model:

    class ViewModel: ObservableObject {
        @Published private(set) var sendFriendRequestViewState = SendFriendRequestViewState.hidden

        func setUp(post: Post) {
            self.post = post
            determineSendFriendRequestViewState()
        }
        
        func determineSendFriendRequestViewState() {
            func isStateSend() -> Bool {
                post.user.isCurrentUser == false &&
                currentUserService.user?.friendIds.contains(post.user.id) == false &&
                currentUserService.user?.sentFriendRequestUserIds.contains(post.user.id) == false
            }
            
            func isStateWaiting() -> Bool {
                currentUserService.user?.friendIds.contains(post.user.id) == false &&
                currentUserService.user?.sentFriendRequestUserIds.contains(post.user.id) == true
            }
            
            if isStateSend() {
                sendFriendRequestViewState = .send
            } else if isStateWaiting() {
                sendFriendRequestViewState = .waiting
            } else {
                sendFriendRequestViewState = .hidden
            }
        }
    }

I realized that if I add .contentLoadingPolicy(.lazy(recyclingRatio: 0)) fixes my issue, but I don’t think it’s a good practice.
Any ideas?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant