-
Notifications
You must be signed in to change notification settings - Fork 67
Implement React.memo #252
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
Implement React.memo #252
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -35,6 +35,64 @@ HookTestComponent(Map props) { | |
]); | ||
} | ||
|
||
final MemoTestDemoWrapper = react.registerComponent(() => _MemoTestDemoWrapper()); | ||
|
||
class _MemoTestDemoWrapper extends react.Component2 { | ||
@override | ||
get initialState => {'localCount': 0, 'someKeyThatMemoShouldIgnore': 0}; | ||
|
||
@override | ||
render() { | ||
return react.div( | ||
{'key': 'mtdw'}, | ||
MemoTest({ | ||
'localCount': this.state['localCount'], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #nit As per the Dart style guide, avoid unnecessary |
||
'someKeyThatMemoShouldIgnore': this.state['someKeyThatMemoShouldIgnore'], | ||
}), | ||
react.button({ | ||
'type': 'button', | ||
'className': 'btn btn-primary', | ||
'style': {'marginRight': '10px'}, | ||
'onClick': (_) { | ||
this.setState({'localCount': this.state['localCount'] + 1}); | ||
}, | ||
}, 'Update MemoTest props.localCount value (${this.state['localCount']})'), | ||
react.button({ | ||
'type': 'button', | ||
'className': 'btn btn-primary', | ||
'onClick': (_) { | ||
this.setState({'someKeyThatMemoShouldIgnore': this.state['someKeyThatMemoShouldIgnore'] + 1}); | ||
}, | ||
}, 'Update prop value that MemoTest will ignore (${this.state['someKeyThatMemoShouldIgnore']})'), | ||
); | ||
} | ||
} | ||
|
||
final MemoTest = react.memo((Map props) { | ||
final context = useContext(TestNewContext); | ||
return react.div( | ||
{}, | ||
react.p( | ||
{}, | ||
'useContext counter value: ', | ||
react.strong({}, context['renderCount']), | ||
), | ||
react.p( | ||
{}, | ||
'props.localCount value: ', | ||
react.strong({}, props['localCount']), | ||
), | ||
react.p( | ||
{}, | ||
'props.someKeyThatMemoShouldIgnore value: ', | ||
react.strong({}, props['someKeyThatMemoShouldIgnore']), | ||
' (should never update)', | ||
), | ||
); | ||
}, areEqual: (prevProps, nextProps) { | ||
return prevProps['localCount'] == nextProps['localCount']; | ||
}, displayName: 'MemoTest'); | ||
|
||
var useReducerTestFunctionComponent = | ||
react.registerFunctionComponent(UseReducerTestComponent, displayName: 'useReducerTest'); | ||
|
||
|
@@ -498,6 +556,13 @@ void main() { | |
useRefTestFunctionComponent({ | ||
'key': 'useRefTest', | ||
}, []), | ||
react.h2({'key': 'memoTestLabel'}, ['memo Test']), | ||
newContextProviderComponent( | ||
{ | ||
'key': 'memoContextProvider', | ||
}, | ||
MemoTestDemoWrapper({}), | ||
), | ||
react.h2({'key': 'useMemoTestLabel'}, ['useMemo Hook Test']), | ||
react.h6({'key': 'h61'}, ['With useMemo:']), | ||
useMemoTestFunctionComponent({ | ||
|
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,163 @@ | ||||
import 'dart:async'; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #nit unused import
Suggested change
|
||||
import 'dart:developer'; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #nit unused import
Suggested change
|
||||
@TestOn('browser') | ||||
import 'dart:html'; | ||||
import 'dart:js_util'; | ||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. #nit unused import
Suggested change
|
||||
|
||||
import 'package:react/react.dart' as react; | ||||
import 'package:react/react_client.dart'; | ||||
import 'package:react/react_test_utils.dart' as rtu; | ||||
import 'package:test/test.dart'; | ||||
|
||||
main() { | ||||
setClientConfiguration(); | ||||
|
||||
Ref<_MemoTestWrapperComponent> memoTestWrapperComponentRef; | ||||
Ref<Element> localCountDisplayRef; | ||||
Ref<Element> valueMemoShouldIgnoreViaAreEqualDisplayRef; | ||||
int childMemoRenderCount; | ||||
|
||||
void renderMemoTest({ | ||||
bool testAreEqual = false, | ||||
String displayName, | ||||
}) { | ||||
expect(memoTestWrapperComponentRef, isNotNull, reason: 'test setup sanity check'); | ||||
expect(localCountDisplayRef, isNotNull, reason: 'test setup sanity check'); | ||||
expect(valueMemoShouldIgnoreViaAreEqualDisplayRef, isNotNull, reason: 'test setup sanity check'); | ||||
|
||||
final customAreEqualFn = !testAreEqual | ||||
? null | ||||
: (prevProps, nextProps) { | ||||
return prevProps['localCount'] == nextProps['localCount']; | ||||
}; | ||||
|
||||
final MemoTest = react.memo((Map props) { | ||||
childMemoRenderCount++; | ||||
return react.div( | ||||
{}, | ||||
react.p( | ||||
{'ref': localCountDisplayRef}, | ||||
props['localCount'], | ||||
), | ||||
react.p( | ||||
{'ref': valueMemoShouldIgnoreViaAreEqualDisplayRef}, | ||||
props['valueMemoShouldIgnoreViaAreEqual'], | ||||
), | ||||
); | ||||
}, areEqual: customAreEqualFn, displayName: displayName); | ||||
|
||||
rtu.renderIntoDocument(MemoTestWrapper({ | ||||
'ref': memoTestWrapperComponentRef, | ||||
'memoComponentFactory': MemoTest, | ||||
})); | ||||
|
||||
expect(localCountDisplayRef.current, isNotNull, reason: 'test setup sanity check'); | ||||
expect(valueMemoShouldIgnoreViaAreEqualDisplayRef.current, isNotNull, reason: 'test setup sanity check'); | ||||
expect(memoTestWrapperComponentRef.current.redrawCount, 0, reason: 'test setup sanity check'); | ||||
expect(childMemoRenderCount, 1, reason: 'test setup sanity check'); | ||||
expect(memoTestWrapperComponentRef.current.state['localCount'], 0, reason: 'test setup sanity check'); | ||||
expect(memoTestWrapperComponentRef.current.state['valueMemoShouldIgnoreViaAreEqual'], 0, | ||||
reason: 'test setup sanity check'); | ||||
expect(memoTestWrapperComponentRef.current.state['valueMemoShouldNotKnowAbout'], 0, | ||||
reason: 'test setup sanity check'); | ||||
} | ||||
|
||||
group('memo', () { | ||||
setUp(() { | ||||
memoTestWrapperComponentRef = react.createRef<_MemoTestWrapperComponent>(); | ||||
localCountDisplayRef = react.createRef<Element>(); | ||||
valueMemoShouldIgnoreViaAreEqualDisplayRef = react.createRef<Element>(); | ||||
childMemoRenderCount = 0; | ||||
}); | ||||
|
||||
tearDown(() { | ||||
memoTestWrapperComponentRef = null; | ||||
localCountDisplayRef = null; | ||||
valueMemoShouldIgnoreViaAreEqualDisplayRef = null; | ||||
}); | ||||
|
||||
group('renders its child component when props change', () { | ||||
test('', () { | ||||
renderMemoTest(); | ||||
|
||||
memoTestWrapperComponentRef.current.increaseLocalCount(); | ||||
expect(memoTestWrapperComponentRef.current.state['localCount'], 1, reason: 'test setup sanity check'); | ||||
expect(memoTestWrapperComponentRef.current.redrawCount, 1, reason: 'test setup sanity check'); | ||||
|
||||
expect(childMemoRenderCount, 2); | ||||
expect(localCountDisplayRef.current.text, '1'); | ||||
|
||||
memoTestWrapperComponentRef.current.increaseValueMemoShouldIgnoreViaAreEqual(); | ||||
expect(memoTestWrapperComponentRef.current.state['valueMemoShouldIgnoreViaAreEqual'], 1, | ||||
reason: 'test setup sanity check'); | ||||
expect(memoTestWrapperComponentRef.current.redrawCount, 2, reason: 'test setup sanity check'); | ||||
|
||||
expect(childMemoRenderCount, 3); | ||||
expect(valueMemoShouldIgnoreViaAreEqualDisplayRef.current.text, '1'); | ||||
}); | ||||
|
||||
test('unless the areEqual argument is set to a function that customizes when re-renders occur', () { | ||||
renderMemoTest(testAreEqual: true); | ||||
|
||||
memoTestWrapperComponentRef.current.increaseValueMemoShouldIgnoreViaAreEqual(); | ||||
expect(memoTestWrapperComponentRef.current.state['valueMemoShouldIgnoreViaAreEqual'], 1, | ||||
reason: 'test setup sanity check'); | ||||
expect(memoTestWrapperComponentRef.current.redrawCount, 1, reason: 'test setup sanity check'); | ||||
|
||||
expect(childMemoRenderCount, 1); | ||||
expect(valueMemoShouldIgnoreViaAreEqualDisplayRef.current.text, '0'); | ||||
}); | ||||
}); | ||||
|
||||
test('does not re-render its child component when parent updates and props remain the same', () { | ||||
renderMemoTest(); | ||||
|
||||
memoTestWrapperComponentRef.current.increaseValueMemoShouldNotKnowAbout(); | ||||
expect(memoTestWrapperComponentRef.current.state['valueMemoShouldNotKnowAbout'], 1, | ||||
reason: 'test setup sanity check'); | ||||
expect(memoTestWrapperComponentRef.current.redrawCount, 1, reason: 'test setup sanity check'); | ||||
|
||||
expect(childMemoRenderCount, 1); | ||||
}); | ||||
}); | ||||
} | ||||
|
||||
final MemoTestWrapper = react.registerComponent(() => _MemoTestWrapperComponent()); | ||||
|
||||
class _MemoTestWrapperComponent extends react.Component2 { | ||||
int redrawCount = 0; | ||||
|
||||
get initialState => { | ||||
'localCount': 0, | ||||
'valueMemoShouldIgnoreViaAreEqual': 0, | ||||
'valueMemoShouldNotKnowAbout': 0, | ||||
}; | ||||
|
||||
@override | ||||
void componentDidUpdate(Map prevProps, Map prevState, [dynamic snapshot]) { | ||||
redrawCount++; | ||||
} | ||||
|
||||
void increaseLocalCount() { | ||||
this.setState({'localCount': this.state['localCount'] + 1}); | ||||
} | ||||
|
||||
void increaseValueMemoShouldIgnoreViaAreEqual() { | ||||
this.setState({'valueMemoShouldIgnoreViaAreEqual': this.state['valueMemoShouldIgnoreViaAreEqual'] + 1}); | ||||
} | ||||
|
||||
void increaseValueMemoShouldNotKnowAbout() { | ||||
this.setState({'valueMemoShouldNotKnowAbout': this.state['valueMemoShouldNotKnowAbout'] + 1}); | ||||
} | ||||
|
||||
@override | ||||
render() { | ||||
return react.div( | ||||
{}, | ||||
props['memoComponentFactory']({ | ||||
'localCount': this.state['localCount'], | ||||
'valueMemoShouldIgnoreViaAreEqual': this.state['valueMemoShouldIgnoreViaAreEqual'], | ||||
}), | ||||
); | ||||
} | ||||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<!DOCTYPE html> | ||
<html> | ||
<head lang="en"> | ||
<meta charset="UTF-8"> | ||
<title></title> | ||
<script src="packages/react/react_with_addons.js"></script> | ||
<script src="packages/react/react_dom.js"></script> | ||
<link rel="x-dart-test" href="react_memo_test.dart"> | ||
<script src="packages/test/dart.js"></script> | ||
</head> | ||
<body> | ||
</body> | ||
</html> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#nit this key is unnecessary