Proposal for Solution to Force Rerender/Selective Render of Nodes
See original GitHub issueIssue
One issue I find appearing in multiple use cases is how to have editor state outside the nodes
force rerendering of nodes.
Here are two use cases that can’t be handled elegantly right now:
-
Editor has an editing mode: For example, the editor has readOnly=false but that doesn’t mean we are editing right now. When the user clicks in the editor, we turn on
editing
mode. When you are inediting
mode, theimage
blocks, for example, might show a “delete” button. This “delete” button should not show when you aren’t editing. -
Highlight blocks: We want to highlight blocks depending on the block keys in an Array.
highlightedBlocks=['abcde', 'fghij']
. The list of highlighted blocks is an Array outside of the nodes. If the highlighted blocks change, the editor doesn’t know and therefore doesn’t change the rendering of the highlighted nodes.
One issue that was solved in another manner is the re-rendering problem when switching both from and to readOnly
mode. My proposal will also discuss an alternate solution that can encompass readOnly
mode as well.
https://github.com/ianstormtaylor/slate/pull/544
Proposal
I feel like a bad solution would be to continue to change shouldNodeComponentUpdate
and keep passing through new values whenever new use cases appear. This is what was done to solve the readOnly
problem.
My proposed solution is to pass in one new value through which is the value
object itself. The value
object has a data
property that we can use to send additional values. I originally thought the solution might be to pass through props or something like that but the beauty of value is that it forces us to ensure that the solution we use can fit into value
which is Immutable which means that Slate continues to stay functional as much as possible.
So in our case of editing mode, when somebody enables editing mode, we set value by doing something like:
const newValue = value.setIn(['data', 'isEditing'], true)
Now, because value would be passed around through the nodes, we can use the plugins shouldNodeComponentUpdate
to force re-render;
function RerenderOnIsEditing() {
return {
shouldNodeComponentUpdate(props, nextProps) {
if (props.value.isEditing !== nextProps.value.isEditing) return true
}
}
}
In order to get readOnly
to work in the same manner, we can take one of two approaches:
-
We could build a compatibility layer where if
props.readOnly
differed fromvalue.data.readOnly
then we immediately call the onChange withvalue.data.readOnly=true
to fix that. -
This might be better for the future, is we can have the outer component do this where basically instead of passing in a
props.readOnly
we have them setvalue.data.readOnly
. The reason why the latter might be preferable is because it gets users into the habit of storing all editor state in thevalue
. This makes it obvious that when they want to add, for example,isEditing
to the state, it should go in the same place thatreadOnly
already exists. The discoverability would be better at the cost of a bit of ease of use.
Issue Analytics
- State:
- Created 6 years ago
- Reactions:1
- Comments:13 (5 by maintainers)
Top GitHub Comments
Hey @thesunny,
Any chance you can share how you did it?
In my renderNode function, I render some nodes depending on the previous and next block type. So when changing a node type, the previous and next one should be re rendered.
Despite the awesome documentation, I was not sure how renderNode was called. If I am right, the nodes rendered are the node focused AND the node who lost the focus. Maybe adding that info to the doc could be helpful.
Here’s my original code for doing a force refresh but I have a newer version posted below but which I haven’t posted (just wrote right now):
The following should work though and uses a command instead.
You should be able to refresh then using this code: