Styled components API - withComponent vs. as - javascript

I've stumbled upon a question regarding the Styled Component API updates in version 4:
withComponent which was handy to use, is now deprecated
as is the introduced alternative to it
But as far as I understood as is meant to be used on a JSX template level whereas withComponent was used within a styled component declaration.
So what is the suggested workflow in situations like following:
const BaseComponent = styled.div`
color: red;
`;
const HeadingComponent = BaseComponent.withComponent('h4');
assuming that we use <HeadingComponent /> in a lot of different places.
Would it mean that instead of having a second styled component, declare a React component using the <BaseComponent as="h4" /> and instead of reusing the styled component, reuse the React component?
So transfer usage of withComponent into creating a new React component using the base styled component with as attribute?
Thanks in advance,
Andreas

While I personally prefer reusing the React component with the as prop, it could be easier for you to just refactor the usages into BaseComponent.attrs({ as: 'h4' })``.

Related

How to specify template for Glimmer Component?

I have a typical Glimmer "base" component:
import Component from '#glimmer/component';
export default class BaseComponent extends Component { ... }
It has a template like normally, but the actual implementations of that component are child componenents, that override some of the template getters and parameters so that it works with various different data types.
export default class TypeAComponent extends BaseComponent { ... }
export default class TypeBComponent extends BaseComponent { ... }
etc.
My question is: How do I specify that all the child components should use the parent class template, so I don't have to duplicate the same fairly complex HTML for all child components? Visually the components are supposed to look identical so any changes would have to be replicated across all child component types. Therefore multiple duplicated templates isn't ideal.
In Ember Classic components there was layout and layoutName properties so I could just do:
layoutName: 'components/component-name'
in the base component and all child components did automatically use the defined template.
Now that I'm migrating to Glimmer components I can't seem to figure out how to do this. I have tried:
layout property
layoutName property
template property
Using the child components without a template in hope that they would automatically fall back to the parent class template.
Only thing that seems to work is creating Application Initializer like this:
app.register('template:components/child1-component', app.lookup('template:components/base-component'));
app.register('template:components/child2-component', app.lookup('template:components/base-component'));
But that feels so hacky that I decided to ask here first if there is a proper way to do this that I have missed?
How to specify template for Glimmer Component?
tl;dr: you should avoid this.
There are two answers to two, more specific, questions:
What is the recommended way to manage complex components with shared behaviors?
Typically, you'll want to re-work your code to use either composition or a service.
Composition
<BaseBehaviors as |myAPI|>
<TypeAComponent #foo={{myAPI.foo}} #bar={{myAPI.bar}} />
<BaseBehaviors>
Where BaseBehaviors' template is:
{{yield (hash
foo=whateverThisDoes
bar=whateverThisBarDoes
)}}
Service
export default class TypeAComponent extends Component {
#service base;
}
and the service can be created with
ember g service base
then, instead of accessing everything on this, you'd access everything on this.base
Ignoring all advice, how do I technically do the thing?
Co-located components (js + hbs as separate files), are combined into one file at build time, which works like this:
// app/components/my-component.js
import Component from '#glimmer/component';
export default class MyComponent extends Cmoponent {
// ..
}
{{! app/components/my-component.hbs }}
<div>{{yield}}</div>
The above js and hbs file becomes the following single file:
// app/components/my-component.js
import Component from '#glimmer/component';
import { hbs } from 'ember-cli-htmlbars';
import { setComponentTemplate } from '#ember/component';
export default class MyComponent extends Cmoponent {
// ..
}
setComponentTemplate(hbs`{{! app/components/my-component.hbs }}
<div>{{yield}}</div>
`, MyComponent);
So this means you can use setComponentTemplate anywhere at the module level, to assign a template to a backing class.
Why is this not recommended over the other approaches?
All of this is a main reason the layout and related properties did not make it in to Octane.
Formally supported Component inheritance results in people getting "clever"
this in of itself, isn't so much of a problem, as it is what people can do with the tool. Bad inheritance is the main reason folks don't like classes at all -- and why functional programming has been on the rise -- which is warranted! Definitely a bit of an over-correction, as the best code uses both FP and OP, when appropriate, and doesn't get dogmatic about this stuff.
Component Inheritance is harder to debug
Things that are a "Foo" but are a subclass of "Foo" may not actually work like "Foo", because in JS, there aren't strict rules around inheritance, so you can override getters, methods, etc, and have them provide entirely different behavior.
This confuses someone who is looking to debug your code.
Additionally, as someone is trying to do that debugging, they'll need to have more files open to try to under stand the bigger picture, which increases cognitive load.
Component inheritance allows folks to ignore boundaries
This makes unit testing harder -- components are only tested as "black boxes" / something you can't see in to -- you test the inputs and outputs, and nothing in between.
If you do want to test the in-between, you need to extract either regular functions or a service (or more rendering tests on the specific things).
I would say this is the classic case for a composition, where TypeAComponent and TypeBComponent use the BaseComponent.
So you have your BaseComponent with all the HTML, that basically is your template. I think its important here to think a bit more of Components also as possible Templates, not only full Components. So lets call this the TemplateComponent.
So you have your TemplateComponent which could also be a template-only component. Then you have as template for TypeAComponent and TypeBComponent:
<TemplateComponent
#type={{#title}}
#title={{#title}}
#onchange={{#onchange}}
#propertyThatIsChanged={{this.propertyThatIsChanged}}
...
/>
this allows you to have a getter propertyThatIsChanged to overwrite pieces. Common behaviour can also be placed on the TemplateComponent, or, if its common code, maybe on a BaseCodeComponent, that only contains shared code, while I would rather not do this.
For areas you want to replace this also opens the possibility to use Blocks.
The TemplateComponent, for example, could use has-block to check if a :title exists, use this block then ({{yield to="default"}}), and if not just use {{#title}}.
So, to the only obvious downside of this: you have to proxy all params. This seems ugly at first, but generally I think its better for components not to have too many arguments. At some point an options or data argument could be better, also because it can be built with js when necessary. It should also be mentioned that there is an open RFC that would address this issue. With the upcoming SFCs, I think this is the much more future-proof solution overall.

How can I apply a base component's propTypes to a styled-component?

Note that I am not using TypeScript, only JavaScript (jsx).
I am trying to style some buttons from a UI library using styled-components. When I try to implement that new styled button, the Intellisense in VSCode does not suggest any of the properties on the original component.
When implementing the Button directly, I can see the icon attribute/definition:
However, when I try creating a styled Button component, Intellisense is no longer functioning:
I have tried setting the defaultProps and propTypes of the styled component to that of the Button component, though this also does not work:
Am I doing something wrong, or is this not possible without TypeScript?
Not sure why is it happening so. Did you can try specifying proptypes like this for StyledButton ? It should work using this approach(even without typescript).
StyledButton.propTypes = {
color: PropTypes.string
};
Instead of assigning it StyledButton.propTypes = Button.propTypes.
You can check more about it on official documentation - documentataion URL

Is there a way to use react-redux (useDispatch) in react using class based components? I am using useDispatch inside render function

Error: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
Hooks are intended to be used in Functional components only. As per the Rules of hooks they can be called from
React function components.
Custom Hooks
In order to other question
Is there a way to use react-redux (useDispatch) in react using class based components?
Yes, there are ways of using react-redux inside class based components. Using connect API provided by react-redux library which will inject the action creators into the class component. Eventually accessing the required dispatch function by this.props. You can have a look here for a basic example of how it can be used.
Hope this helps.
You cannot use hooks with class components. As written in the docs, you need to create a container/wrapper component that passes the actions.
For that you have to use connect from 'react-redux' like this:
import { connect } from 'react-redux'
const mapDispatchToProps = dispatch => {
return {
onTodoClick: id => {
dispatch(toggleTodo(id))
}
}
}
const VisibleTodoList = connect(mapStateToProps (HERE NOT NEEDED; IF YOU WANT TO DISPATCH ONLY), mapDispatchToProps)(TodoList)
export default VisibleTodoList
This would pass onTodoClick to your component via props which you can just call like any prop: this.props.onTodoClick()

When creating React components should I use functional components, React.createClass or extends React.Component?

I am quite new to React and have been reading up a lot about it. I have come across three different methods to create components:
Functional Components:
const Hello = ({world}) => {
return (
<div>{"Hello" + world}</div>
);
}
React.createClass (Factory):
const Hello = React.createClass({
render: function() {
return <div>{"Hello" + this.props.world}</div>
}
});
ES6 Class Extends
class Hello extends React.Component {
render() {
return (
<div>{"Hello" + this.props.world}</div>
);
}
}
Apart from the obvious that the functional components don't have any state coupled to it and is probably a more functional approach to doing components, why would I use the different methods?
In the React documentation they use all three methods. Some of the Stack Overflow articles suggest the Class method and then some suggest the Factory method.
Using functional is recommended for components that are stateless.
Using React.Component extend is recommended if you are working with ES6+/TypeScript code. React Native has support only for this type of component creation.
React.createClass is for used with ES5.
Browsers will soon have a complete support and Facebook recommends to use React.Component instead of React.createClass and to use functional for stateless.
Edit
Facebook added a deprecation warning to React.createClass in React 15.6.0 and points users to use create-react-class instead.
As I know, it is recommend to use functional component when you need just create presentational logic component. For component that has any logic we use smth like React.createClass().
There are two ways of doing that:
First, you create general component and than divide it on presentational logic and business logic.
Second, you can create them alongside.
Be careful! When divide these components, your business logic must render your presentational logic!
For more practice see tutorials on the codecademy

Two ways of defining ES6 React Components

I was looking at this fiddle for MobX and I've seen these two ways of defining React Components in ES6 other places as well, like Dan Abramov's egghead redux video series.
#observer
class TodoListView extends Component {
render() {
return <div>
<ul>
{this.props.todoList.todos.map(todo =>
<TodoView todo={todo} key={todo.id} />
)}
</ul>
Tasks left: {this.props.todoList.unfinishedTodoCount}
</div>
}
}
const TodoView = observer(({todo}) =>
<li>
<input
type="checkbox"
checked={todo.finished}
onClick={() => todo.finished = !todo.finished}
/>
<input
type="text"
value={todo.title}
onChange={ e => todo.title = e.target.value } />
</li>
);
My question is, when is it appropriate to use each type?
It seems like the simpler components are able to use the simpler syntax, but I'd like a rule or guideline to follow.
Thanks!
The second pattern is called "stateless functional components", and using it is recommended in almost all cases. SFCs (stateless functional components) are pure functions that are only dependent on their props. They are easier to test, decoupled from each other and will have significant performance gains over other patterns in the future. (source from the official react documentation)
They have a few gotchas though, namely:
One cannot attach refs to SFCs. (src, src2)
They cannot have internal state. (src)
They cannot use lifecycle methods. (e.g. componentDidMount, src)
If you need any of these things, first make sure there is no way around using them and only then use either the ES6 class or the React.createClass patterns.
I highly recommend "Should I use React.createClass, ES6 Classes or stateless functional components?" by James K Nelson to understand the tradeoffs and difference between these patterns, and "Presentational and Container Components" by Dan Abramov for an explanation of the most commonly used structure for Redux applications.
React.createClass VS ES2015 classes VS functional stateless components
In React, there are 3 main ways of creating a new component.
The first one to be introduced alongside with React library was React.createClass, which has the following syntax:
var TestComponent = React.createClass({
render: function(){
return <p>{this.props.children}</p>;
}
})
React.render(<TestComponent>This will be a paragraph element!</TestComponent>, document.body);
After that, in React 0.13 release, we could define our components directly as ES2015 classes:
class TestComponent extends React.Component {
render () {
return <p>{this.props.children}</p>;
}
}
React.render(<TestComponent>This will be a paragraph element!</TestComponent>, document.body);
React 0.14 introduces the ability to create those called stateless components, also known as functional or pure components because they are declared as a function that has no state and returns the same markup given the same props:
const TestComponent = (props) => <p>props.children</p>;
ReactDOM.render(<TestComponent>This will be a paragraph element!</TestComponent>,
document.querySelector('#root'));
These stateless components are much more lighter for React to render, and also have the advantage of being React agnostic, meaning that they can be rendered in any other way given the same input and will produce the same output.
So what you should use in your app depends. Each has their pros and cons and should be used under certain conditions. There are times when you can't use stateless components.
Use ES2015 (ES6) classes or React.createClass when your component needs lifecycle methods or you need to access 'this' for anything other than props.

Categories