Inserter - fix block manager state handling#95
Inserter - fix block manager state handling#95mzorz wants to merge 9 commits intofeature/inserterfrom
Conversation
…irectly but through props and state
…, content or addition/deletion happened
| static isAdditionOrDeletion( newProps: PropsType, currState: StateType ) { | ||
| // there's been an addition / deletion | ||
| if ( currState.dataSource.size() !== newProps.blocks.length ) { | ||
| return true; |
There was a problem hiding this comment.
This method is returning true in one case, but is missing the return value for the else execution path.
|
Related to this, I think #93 was only necessary because of a similar issue as the Do you think it's worth including that change in this one? |
If you mean to bring the change in #93 to this branch here, that change is already in this branch as it's been updated - also I just reviewed again your change on #93 and IIUC I don't think it's adding anything in particular that we should revert - the PR does what its advertising, i.e. it acts as something for the developer to not pay special attention to which properties should be set, which comes in handy. In this sense I believe your change should be harmless, unless I'm missing something? i.e could we have some properties overriden when we don't want to? wdyt? Also, bringing part of a convo we had on Slack that may be worth to clarify here as well, in terms of what gets saved to the redux store and how the datasource is handled: the data source itself is not part of the |
|
I can't say i'm fond of recreating the DataSource solution. This effectively disables the animations, nullifying one of the main drives behind using the Allow me to investigate a bit the root cause behind the "focus" issue and report back. |
Yes, we still want that functionality, but the redux store was already doing that for us. The problem was that we were also setting the attributes in the data source directly, overriding what was in the store. This is what I'd propose: |
Me neither 😆
Please do! this is exactly the reason for this PR to exist 👍 Thanks in advance 🙇 |
Oh I see now 😄 ! Sorry I didn't realize - will do and get back here |
|
Closing this in favor of #108 - this comment #95 (comment) doesn't apply anymore because of the reasons explained in #108 |
…-first-word-selected-ios Blocks no longer merge when pressing backspace with a non-zero-length selection.
This PR brings the ability to focus/move/delete/create blocks back to #88 by making sure the datasource used for the RecyclerView gets re-built and set through the Block Manager component
statelifecycle rather than operating on the List'sdatasourcein a direct way, which I believe was an early optimization done to explore the ability for the RecyclerView list RN wrapper to support animations (i.e. make blocks change positions animatedly while on another level reflecting the changes to the model). IIRC these explorations were done before the Redux connection was implemented.Before
(note how tapping on one of the newly created blocks at the bottom of the list does not make the toolbar appear - that's an indication the
focusedprop isfalse)After
(note how tapping on any block makes it be focused, making the toolbar appear)
The flow / lifecycle in this PR - instead of setting the items in the datasource directly, it only modifies the component's
propson each action (move up/down, delete and create), which then makes it to the redux store and finally then receives the new information through ~componentWillReceiveProps~in commit a12f5ac first, but then moved to the newstatic getDerivedStateFromProps()in 5a5a2cc and finally sets the new state there, effectively creating a new dataset for our forked RN RecyclerView.In order to conveniently choose whether deriving and setting a new state is appropriate or not, there are 2 new specific methods
isAdditionOrDeletionwhich checks whether there's a new block created or a block has been deleted, andisFocusContentPositionChangewhich checks whether a change happened infocus,content, or the position of any block.Description / Background / Path to solution
Since the merge of #81 in commit f47cc5d, I observed an issue only when adding new blocks in PR #88 and when these blocks are implemented by the
<Rich>component, which in native is in turn implemented by the AztecRN wrapper.1 - blocks generated in the initial state can handle get/lose
focusedstate just fine2 - how new blocks were generated made no difference - tried to generate blocks with
parse(),createBlockand manually hardcoding the values, all work well with initial state but don't work well when inserted to the datasource on the fly3 - this started to fail only when I merged back the new Aztec based
Richcomponent implementation (PR #81)4 - the insertion and state keeping work alright when we use a plain object instead of the Rich component, i.e. by setting this line here to
if (false)https://github.com/wordpress-mobile/gutenberg-mobile/blob/try/inserter-take1/src/block-management/block-holder.js#L49As per my tests, the problem only appeared under these conditions:
focusedprop change that will hit redux, butwhen the list is re-rendered the change is not made visible, and by observation it turns out that's because the prop there will still be
focused=false. Why is itfalse? I couldn't understand why.Looking into all corners of the code I suspected the problem is manipulation of state in two places: locally on the List's state
datasourceto produce the UI changes, and on the props and signaling the redux store, not really then making good use of the lifecycle.For example, this was being done on
DELETEaction:.
Making sure the datasource was not directly accessed / modified was a first step (eliminating line 1 within the case handler above), and then making sure the state was then set after props changed made sense as part of the component's lifecycle.
Possible next steps
In the near future we might want to check how to bring some optimization back into the List (if more than say 100 or 200 blocks ever exist into a single Post), and animations can be looked into later as well. I think the way we were handling things was not being done properly. The obvious thing is, we are indeed producing a new
datasourceinstead of only changing the items that need be changed but that's how React should work - down the line the properComponents(items in the list) should decide wether to update themselves or not within the List, but the whole state needs to be a new object each time as per how React works.Other resources to have in mind as we discover and adapt our block manager to further uses:
https://facebook.github.io/react-native/docs/direct-manipulation
https://reactjs.org/docs/react-component.html#shouldcomponentupdate
https://reactjs.org/blog/2018/06/07/you-probably-dont-need-derived-state.html#recommendation-fully-controlled-component
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#new-lifecycle-getderivedstatefromprops
https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html#updating-state-based-on-props
Further notes
Just adding many reviewers so to get a few of your eyes on this - it seems important and would like to get the team aware and get as many comments as possible :) 🙇