@@ -42,14 +42,14 @@ var QuillComponent = React.createClass({
42
42
) return new Error (
43
43
'Since v1.0.0, React Quill will not create a custom toolbar for you ' +
44
44
'anymore. Create a toolbar explictly, or let Quill create one. ' +
45
- 'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0 '
45
+ 'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v100 '
46
46
) ;
47
47
} ,
48
48
49
49
toolbar : function ( props ) {
50
50
if ( 'toolbar' in props ) return new Error (
51
51
'The `toolbar` prop has been deprecated. Use `modules.toolbar` instead. ' +
52
- 'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v1-0-0 '
52
+ 'See: https://github.com/zenoamaro/react-quill#upgrading-to-react-quill-v100 '
53
53
) ;
54
54
} ,
55
55
@@ -94,105 +94,156 @@ var QuillComponent = React.createClass({
94
94
} ,
95
95
96
96
/*
97
- Changing one of these props should cause a re-render.
97
+ Changing one of these props should cause a full re-render.
98
98
*/
99
99
dirtyProps : [
100
- 'id' ,
101
- 'className' ,
102
- 'style' ,
103
100
'modules' ,
104
101
'formats' ,
105
102
'bounds' ,
106
103
'theme' ,
107
104
'children' ,
108
105
] ,
109
106
107
+ /*
108
+ Changing one of these props should cause a regular update.
109
+ */
110
+ cleanProps : [
111
+ 'id' ,
112
+ 'className' ,
113
+ 'style' ,
114
+ 'placeholder' ,
115
+ 'onKeyPress' ,
116
+ 'onKeyDown' ,
117
+ 'onKeyUp' ,
118
+ 'onChange' ,
119
+ 'onChangeSelection' ,
120
+ ] ,
121
+
110
122
getDefaultProps : function ( ) {
111
123
return {
112
- className : '' ,
113
124
theme : 'snow' ,
114
125
modules : { } ,
115
126
} ;
116
127
} ,
117
128
118
129
/*
119
- We consider the component to be controlled if
120
- whenever `value` is being sent in props.
130
+ We consider the component to be controlled if `value` is being sent in props.
121
131
*/
122
132
isControlled : function ( ) {
123
133
return 'value' in this . props ;
124
134
} ,
125
135
126
136
getInitialState : function ( ) {
127
137
return {
138
+ generation : 0 ,
128
139
value : this . isControlled ( )
129
140
? this . props . value
130
141
: this . props . defaultValue
131
142
} ;
132
143
} ,
133
144
134
- componentWillReceiveProps : function ( nextProps ) {
145
+ componentWillReceiveProps : function ( nextProps , nextState ) {
146
+ // If we need to regenerate the component, we can avoid a detailed
147
+ // in-place update step, and just let everything rerender.
148
+ if ( this . shouldComponentRegenerate ( nextProps , nextState ) ) {
149
+ return this . regenerate ( ) ;
150
+ }
151
+
135
152
var editor = this . editor ;
153
+
136
154
// If the component is unmounted and mounted too quickly
137
155
// an error is thrown in setEditorContents since editor is
138
156
// still undefined. Must check if editor is undefined
139
157
// before performing this call.
140
- if ( editor ) {
141
- // Update only if we've been passed a new `value`.
142
- // This leaves components using `defaultValue` alone .
143
- if ( 'value' in nextProps ) {
144
- // NOTE: Seeing that Quill is missing a way to prevent
145
- // edits, we have to settle for a hybrid between
146
- // controlled and uncontrolled mode. We can't prevent
147
- // the change, but we'll still override content
148
- // whenever `value` differs from current state.
149
- if ( nextProps . value !== this . getEditorContents ( ) ) {
150
- this . setEditorContents ( editor , nextProps . value ) ;
151
- }
158
+ if ( ! editor ) return ;
159
+
160
+ // Update only if we've been passed a new `value` .
161
+ // This leaves components using `defaultValue` alone.
162
+ if ( 'value' in nextProps ) {
163
+ // NOTE: Seeing that Quill is missing a way to prevent
164
+ // edits, we have to settle for a hybrid between
165
+ // controlled and uncontrolled mode. We can't prevent
166
+ // the change, but we'll still override content
167
+ // whenever ` value` differs from current state.
168
+ if ( nextProps . value !== this . getEditorContents ( ) ) {
169
+ this . setEditorContents ( editor , nextProps . value ) ;
152
170
}
153
- // We can update readOnly state in-place.
154
- if ( 'readOnly' in nextProps ) {
155
- if ( nextProps . readOnly !== this . props . readOnly ) {
156
- this . setEditorReadOnly ( editor , nextProps . readOnly ) ;
157
- }
171
+ }
172
+
173
+ // We can update readOnly state in-place.
174
+ if ( 'readOnly' in nextProps ) {
175
+ if ( nextProps . readOnly !== this . props . readOnly ) {
176
+ this . setEditorReadOnly ( editor , nextProps . readOnly ) ;
158
177
}
159
178
}
160
179
} ,
161
180
162
181
componentDidMount : function ( ) {
163
- var editor = this . createEditor (
182
+ this . editor = this . createEditor (
164
183
this . getEditingArea ( ) ,
165
184
this . getEditorConfig ( )
166
185
) ;
167
- this . editor = editor ;
186
+ // Restore editor from Quill's native formats in regeneration scenario
187
+ if ( this . quillDelta ) {
188
+ this . editor . setContents ( this . quillDelta ) ;
189
+ this . editor . setSelection ( this . quillSelection ) ;
190
+ this . editor . focus ( ) ;
191
+ this . quillDelta = this . quillSelection = null ;
192
+ return ;
193
+ }
194
+ if ( this . state . value ) {
195
+ this . setEditorContents ( this . editor , this . state . value ) ;
196
+ return ;
197
+ }
168
198
} ,
169
199
170
200
componentWillUnmount : function ( ) {
171
- // NOTE: Don't set the state to null here
172
- // as it would generate a loop.
173
- var e = this . getEditor ( ) ;
174
- if ( e ) this . unhookEditor ( e ) ;
201
+ var editor ; if ( ( editor = this . getEditor ( ) ) ) {
202
+ this . unhookEditor ( editor ) ;
203
+ this . editor = null ;
204
+ }
175
205
} ,
176
206
177
207
shouldComponentUpdate : function ( nextProps , nextState ) {
178
- // Rerender whenever a "dirtyProp" changes
179
- var props = this . props ;
208
+ var self = this ;
209
+
210
+ // If the component has been regenerated, we already know we should update.
211
+ if ( this . state . generation !== nextState . generation ) {
212
+ return true ;
213
+ }
214
+
215
+ // Compare props that require React updating the DOM.
216
+ return some ( this . cleanProps , function ( prop ) {
217
+ // Note that `isEqual` compares deeply, making it safe to perform
218
+ // non-immutable updates, at the cost of performance.
219
+ return ! isEqual ( nextProps [ prop ] , self . props [ prop ] ) ;
220
+ } ) ;
221
+ } ,
222
+
223
+ shouldComponentRegenerate : function ( nextProps , nextState ) {
224
+ var self = this ;
225
+ // Whenever a `dirtyProp` changes, the editor needs reinstantiation.
180
226
return some ( this . dirtyProps , function ( prop ) {
181
- return ! isEqual ( nextProps [ prop ] , props [ prop ] ) ;
227
+ // Note that `isEqual` compares deeply, making it safe to perform
228
+ // non-immutable updates, at the cost of performance.
229
+ return ! isEqual ( nextProps [ prop ] , self . props [ prop ] ) ;
182
230
} ) ;
183
231
} ,
184
232
185
233
/*
186
- If for whatever reason we are rendering again,
187
- we should tear down the editor and bring it up
188
- again.
234
+ If we could not update settings from the new props in-place, we have to tear
235
+ down everything and re-render from scratch.
189
236
*/
190
- componentWillUpdate : function ( ) {
191
- this . componentWillUnmount ( ) ;
237
+ componentWillUpdate : function ( nextProps , nextState ) {
238
+ if ( this . state . generation !== nextState . generation ) {
239
+ this . componentWillUnmount ( ) ;
240
+ }
192
241
} ,
193
242
194
- componentDidUpdate : function ( ) {
195
- this . componentDidMount ( ) ;
243
+ componentDidUpdate : function ( prevProps , prevState ) {
244
+ if ( this . state . generation !== prevState . generation ) {
245
+ this . componentDidMount ( ) ;
246
+ }
196
247
} ,
197
248
198
249
getEditorConfig : function ( ) {
@@ -222,6 +273,19 @@ var QuillComponent = React.createClass({
222
273
return this . state . selection ;
223
274
} ,
224
275
276
+ /*
277
+ Regenerating the editor will cause the whole tree, including the container,
278
+ to be cleaned up and re-rendered from scratch.
279
+ */
280
+ regenerate : function ( ) {
281
+ // Cache selection and contents in Quill's native format to be restored later
282
+ this . quillDelta = this . editor . getContents ( ) ;
283
+ this . quillSelection = this . editor . getSelection ( ) ;
284
+ this . setState ( {
285
+ generation : this . state . generation + 1 ,
286
+ } ) ;
287
+ } ,
288
+
225
289
/*
226
290
Renders an editor area, unless it has been provided one to clone.
227
291
*/
@@ -230,22 +294,26 @@ var QuillComponent = React.createClass({
230
294
var children = this . props . children ;
231
295
232
296
var properties = {
297
+ key : this . state . generation ,
233
298
ref : function ( element ) { self . editingArea = element } ,
234
- dangerouslySetInnerHTML : { __html :this . getEditorContents ( ) }
235
299
} ;
236
300
237
- if ( React . Children . count ( children ) === 0 ) {
238
- return React . DOM . div ( properties ) ;
239
- }
301
+ var customElement = React . Children . count ( children )
302
+ ? React . Children . only ( children )
303
+ : null ;
304
+
305
+ var editingArea = customElement
306
+ ? React . cloneElement ( customElement , properties )
307
+ : React . DOM . div ( properties ) ;
240
308
241
- var editor = React . Children . only ( children ) ;
242
- return React . cloneElement ( editor , properties ) ;
309
+ return editingArea ;
243
310
} ,
244
311
245
312
render : function ( ) {
246
313
return React . DOM . div ( {
247
314
id : this . props . id ,
248
315
style : this . props . style ,
316
+ key : this . state . generation ,
249
317
className : [ 'quill' ] . concat ( this . props . className ) . join ( ' ' ) ,
250
318
onKeyPress : this . props . onKeyPress ,
251
319
onKeyDown : this . props . onKeyDown ,
0 commit comments