Skip to content
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

Avoid work inside of render() #50

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
39 changes: 25 additions & 14 deletions src/withStyles.jsx
Expand Up @@ -37,14 +37,33 @@ export function withStyles(
const styleDef = styleFn && ThemedStyleSheet.create(styleFn);
const BaseClass = baseClass(pureComponent);

function getAddedProps(themeName) {
const addedProps = {
[themePropName]: ThemedStyleSheet.get(themeName),
};

if (styleDef) {
addedProps[stylesPropName] = styleDef(themeName);
}

return addedProps;
}

return function withStylesHOC(WrappedComponent) {
// NOTE: Use a class here so components are ref-able if need be:
// eslint-disable-next-line react/prefer-stateless-function
class WithStyles extends BaseClass {
render() {
const props = this.props;
const { themeName } = this.context;
constructor(props, context) {
super(props, context);

this.state = getAddedProps(context.themeName);
}

componentWillReceiveProps(nextProps, nextContext) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is ThemedStylesheet.get guaranteed to be idempotent based on the themeName?

If not, you may need to call it unconditionally in cWRP.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, it's guaranteed. get(x) === get(x). always. Similarly, styleDef(x) === styleDef(x) always, and only does a meaningful amount of work (ie, more than a hash lookup) on the first call. As a result, i'm not really sure that this PR will actually have a meaningful impact on performance, and could even be negative, due to the extra lifecycle events. Have we benchmarked any of this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't benchmarked yet. I plan to do that on Monday though!

if (this.context.themeName !== nextContext.themeName) {
this.setState(getAddedProps(nextContext.themeName));
}
}

render() {
// As some components will depend on previous styles in
// the component tree, we provide the option of flushing the
// buffered styles (i.e. to a style tag) **before** the rendering
Expand All @@ -56,15 +75,7 @@ export function withStyles(
ThemedStyleSheet.flush();
}

const addedProps = {
[themePropName]: ThemedStyleSheet.get(themeName),
};

if (styleDef) {
addedProps[stylesPropName] = styleDef(themeName);
}

return <WrappedComponent {...props} {...addedProps} />;
return <WrappedComponent {...this.props} {...this.state} />;
}
}

Expand Down
30 changes: 30 additions & 0 deletions test/withStyles_test.jsx
Expand Up @@ -120,6 +120,36 @@ describe('withStyles()', () => {
shallow(<Wrapped />, { context: { themeName: 'tropical' } }).dive();
});

it('reacts to changed context', () => {
const tropicalTheme = {
color: {
red: 'yellow',
},
};
ThemedStyleSheet.registerTheme('tropical', tropicalTheme);

function MyComponent() {
return null;
}

const Wrapped = withStyles(({ color }) => ({
foo: {
color: color.red,
},
}))(MyComponent);
const wrapper = shallow(<Wrapped />, { context: { themeName: 'default' } });

// default theme
expect(wrapper.state('styles'))
.to.eql({ foo: { color: '#990000' } });

wrapper.setContext({ themeName: 'tropical' });

// tropical theme
expect(wrapper.state('styles'))
.to.eql({ foo: { color: 'yellow' } });
});

it('allows the styles prop name to be customized', () => {
function MyComponent({ bar }) {
expect(bar).to.eql({ foo: { color: '#ff0000' } });
Expand Down