Does anyone know how to share some of the parent state with child state, in addition to having custom state properties in the child state, while maintaining propagation of any state changes? Thanks!
Example:
struct RootState: Equatable {
var books: IdentifiedArrayOf<Book> = []
var addBook: AddBookState {
get {
AddBookState(books: books)
// ISSUE: `searchResults` custom state cannot be set here, thus getting lost
}
set {
self.books = newValue.books
}
}
}
struct AddBookState: Equatable {
// shared state
var books: IdentifiedArrayOf<Book>
// custom, "AddBook" domain-specific state
var searchResults: [String] = []
}
AddBookState can add a new book to the shared books state. However, it also needs an additional custom state, such as searchResults. Since the AddBookState is a computed property to ensure the propagation of changes made to books, I don't know how to maintain the values of the custom searchResults state property.
"Local state" still needs to be brought back to the root state at some point, or it will indeed be lost. I'd put your searchResults 'state' in your RootState, then pass both down to your AddBookState in your getter and set them in your setter.
The idea, I believe, is to create “feature” states that encapsulate other states. All state has to flow top to bottom (and back), so if it’s missing at the top, it will never propagate down properly.
Hm, interesting. With that, I'm guessing you would pass the whole parent state into a particular view, and scope it down to appropriate parts of a state, as needed.
Something like this?
// MARK: Parent
struct ParentState: Equatable {
var books: [Book] = []
var search = SearchState()
}
enum ParentAction: Equatable {
var search(SearchAction)
...
}
// MARK: Search
struct SearchState: Equatable {
var searchResults: [String] = []
...
}
enum SearchAction: Equatable {
...
}
// MARK: AddBookView
struct AddBookView: View {
let store: Store<ParentState, ParentAction>
var body: some View {
WithViewStore(store.scope(state: { $0.search }, action: ParentAction.search)) { searchViewStore in
// use `searchViewStore` to access search state, and send SearchAction actions
WithViewStore(store.scope(state: { $0.books })) { booksViewStore in
// use `booksViewStore` to access books state, and send ParentAction actions
}
}
}
}
If AddBookView has it all needs between these two, then I don't see why this wouldn't work. I agree that having an AddBookState that is carbon-copy of the root state is redundant, but you know more about how each of these will potentially grow.