-
Notifications
You must be signed in to change notification settings - Fork 95
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
Add optional argument to withStyles
allowing arbitrary data to be passed to the plugin
#86
Comments
I'd like to understand your use case some more. We don't use the term plugin in this project, so I'm curious what you mean by that. Also, can you give some examples of what this would look like for consumers and any alternatives that you have considered? |
Many components can use By allowing the user to provide a string (typically the component name) to the
I wanted to make explicit the distinction when referring to the interface defined by react-with-styles for the interfaces (plugins) to implement, and the interfaces (plugins) themselves. I see we use the term interfaces in the readme. So this may not be the time to introduce new terminology. I just want to make sure it's clear when I'm referring to functionality that is offered by the interface react-with-styles defines, versus the individual interfaces, each of which may have its own independent set of concerns.
// MyComponent.jsx
export default withStyles(myStylesThunk, 'MyComponent')(MyComponent);
// interface.js
function create(styles, data) {
const stylesToClassNames = {};
Object.keys(styles).forEach((styleName) => {
stylesToClassNames[styleName] = `${data}--${styleName}`;
});
return stylesToClassNames;
}
It would be nice, for this use case, if it were possible to reliably extract the name of the component across different environments. However, as we've discussed, there does not appear to be a reliable method to do so. The main criterion is being able to construct deterministic class names. This essentially requires reliance upon some artifact of the component, such as the component name. I think allowing the user to explicitly provide a unique namespace on a per-component basis will be less prone to issues. The explicitness prevents a situation where the user changes some property of the component (such as the component name) and inadvertently breaks the association of static CSS classes to that component. |
Is the name not something that could be inferred directly from the component? It would still need to be provided to the interface, but I'm concerned this would create a situation where every single call to withStyles is not identical which would defeat the purpose of making such components using withStyles 100% reuseable. |
@lencioni can comment on this more in-depth. But the short answer is, not reliably. One of the issues is in the browser babel will have stripped away the name/displayName information which is necessary to construct the correct class names. Working around that imposes additional external requirements on the user's build process. It also implicitly assumes the user's component names are unique.
I agree this is an important concern as it harms interchangeability. The worst outcome with the current proposal is the user might have to add/change the second argument to all their I think the underlying issue is that of namespacing CSS, which is by no means a new problem. Here are the three approaches I see: Approach ARequire a unique string as a second argument to Styles passed to Uniqueness + determinism is not a requirement of all interfaces (e.g. aphrodite). But it is a requirement for some interfaces (e.g. static CSS output). This approach improves interchangeability of interfaces by requiring a unique identifier per component regardless of the interface. Thus, styles that work with the aphrodite interface but are not fully unique won't have to be renamed in order to work with an interface that requires the styles it's given be unique. Approach BLeave the interface as it is. Why? If the user never uses an interface requiring unique style names be given to Likewise, if the user adheres to a style naming convention that ensures uniqueness of style names across components they will never run into an issue. If the user writes non-unique style names and (possibly in the distant future) wishes to use an interface that requires unique style names be given to Approach CAllow an optional, unique string as a second argument to It's backwards-compatible. It doesn't force users to provide the argument to interfaces that don't need it. At the same time, if the user later chooses to use an interface that does require such an argument, they can add it to all their Note that this approach is a narrower version of the initial proposal. The initial proposal created far more flexibility in the interface with regard to what arguments could be passed and thus would have harmed the general interchangeability of interfaces by encouraging interface-specific arguments. With the goals of improving interchangeability of interfaces while maintaining uniformity across The arguments against Approach A are it introduces a major change and it requires slightly more input from the user per call to |
I think approach C is a non-starter for the reasons I mentioned - react-with-styles only works because components can always be written without the interface in mind, and it's every interface's responsibility to adapt to that. With Approach B, would you be able to, at runtime, throw errors when collisions are detected? |
I believe you could. It would require implementing functionality to keep track of what class names have been exposed via a Additionally, presenting a warning/error when pre-compiling the static CSS would be trivial.
I think what we're finding is react-with-styles and existing interfaces support style names that are not guaranteed to be unique + deterministic across all components. However, some interfaces will work incorrectly without that requirement and (as far as we've found) have no way to adapt. Thus, a user wishing to use the CSS interface we're discussing would have to write their component's styles with the interface in mind. |
If that's the case, then that sounds like something react-with-styles should try to fix internally, without having to require users provide additional information (if possible). |
That's definitely the ideal solution. I think it boils down to the following question: Is there a deterministic + unique identifier associated with every component that can be reliably accessed on both the browser and server? |
perhaps its resolved import path, but there's no way to get at that unless it's provided manually or by a babel transform in the original code. |
@lencioni suggested a babel transform for preserving the name/displayName in the browser bundle. But similarly, it sounds like that would impose additional requirements on the user's build process. |
True. However, that seems like a reasonable requirement imo for debugging anyways. |
For debugging? I don't follow. facebook/react#1137 has discussion on this topic but doesn't offer a solution. This feature has been requested for a while. |
Yes, component names could then be attached to stack traces, and show up in dev tools. |
I am actually most the fan of option C. @ljharb can you elaborate a bit more on why it might be inherently bad to have I think it would be helpful to talk more specifics in this scenario. When we move
if we deterministically created class names from this object, we'd get two conflicting When we convert our own packages to use
to keep them unique, but it'd be nice if there was an easy way to convert existing packages or even our own for use with the CSS interface. The ideal of course is if we can just use the component name but again, that gets stripped out at runtime when using something like babel (which is common). If we could just pass in a name for each set of styles that can be ignored by most interfaces, that seems like the best case scenario, no? e.g.
|
The reason that would be bad is that we'd end up with a subset of withStyles components that worked with some interfaces, and a different subset that worked with others. The explicit goal of withStyles is that 100% of components work with 100% of interfaces, without modifications to any components. |
@fvgs @ljharb @lencioni So I talked a bit more with @ljharb about this, and the conclusion that we came to is that it is probably unnecessary to add this functionality. In the CSS interface, we should prepend a component's The primary reason that we can't always rely on component Thoughts? |
@majapw Was there anything added to the Readme regarding an issue around the uglify babel plugin for the component I've followed the direction on your comment to add an option to add |
@jeremy-clearlabs a PR to add that note would be very appreciated! |
I propose modifying
withStyles
to accept an optionaldata
argument to be passed through to the plugin for the plugin's use. Thedata
argument would be passed to thecreate
method of the interface.Implementing this as an arbitrary and optional data argument maintains backwards-compatibility and allows any plugin now or in the future to make use of the feature for its own purposes.
The use case on which I'm currently working benefits from allowing the user to pass an optional string to
withStyles
. The purpose of the string is to uniquely identify the component consuming the styles relative to other components also usingwithStyles
.The text was updated successfully, but these errors were encountered: