This is a simplified code.
Here is a button component:
import React from 'react'
import styles from './Button.module.scss'
const Button = (props) => {
return (
<button type={props.type} className={styles.btn} onClick={props.handleClick}>{props.text}</button>
)
}
export default Button
I'd like to reuse this component in multiple places. My issue is that i have a form where i don't need to provide any handleClick logic. So i just simply omit it:
<form onSubmit={handleFormSubmit} >
*form related code*
<Button text="Submit" type="submit" />
<form/>
And in places where i need some handleClick logic i pass it:
<Button text="Do Something" type="button" handleClick={()=> console.log('clicked')} />
Is it a correct way to reuse a component or should i have 2 different components in this case? It does not feel ok to make it reusable by not passing props. Thank you.
It's perfect, in the matter of fact, making a component re-usable via it's props is the best practice, and unless this way makes a component unnecessarily complicated then don't use it, and go for the Two Components approach instead.
Tip: we have a component A, it's already re-usable, but, we want to add another feature to it, to support an other use case, but it'll make the component complicated, in this case, building a new component A2 is preferable.
I believe that it is completely fine to not pass props to components. In a large number of libraries, there are default props. In the PropTypes library, you can use defaultProps to provide default values so the developer does not have to specify them every time.
Sometimes it is difficult to decide. So I always go with intent. Every component has an intent/purpose. If we use a property to customize the behavior slightly, then it is completely fine. Button with an optional handle for click binding.
But when we do with some important properties which could make the component behave like a different component (different intent), then it is better to go with two components to avoid ambiguity in usage. like ClickableButton and SimpleButton.
Related
I want to create a customizable avatar upload component like this:
<AvatarUpload
...
renderSaveButton=({disabled}) => <Button disabled={disabled}>...</Button>
/>
As you can see the renderSaveButton function defines how the button should be rendered. More importantly, it also receives a parameter from the internal state of the AvatarUpload component.
Then in the AvatarUpload component itself, I render the button this way:
{renderSaveButton({disabled: value)}
The problem is that the linter is complaining that React is going to create a new function on every render, and I should not do it (the message is "do not define components during render").
Shall I care about this warning, and if so what's the alternative?
I searched on google but it's not clear to me what's the alternative to this render props technique.
If there's no measurable performance hit using an inline anonymous callback function then I don't see any issue with this. It's just a warning.
If there is a performance issue or you just really want to clear out all warnings then you can memoize the callback that is passed to children components using the useCallback hook.
Example:
const renderSaveButton = useCallback(({ disabled }) => (
<Button disabled={disabled}>
...
</Button>
), [/* place any dependencies here */]);
...
<AvatarUpload ... renderSaveButton={renderSaveButton} />
I am currently working in a scenario, where I need to be able to import a component from library, but tell it to choose different components for some of its child components to render with. In this case, it needs to choose different button components, for example. Now I already got this working, as in, it does what it needs to do, but I am wondering if there is maybe a more fitting/appropriate way of doing it.
export const container = ({component, children}) => {
const ButtonComponent = component?.button ?? Button;
return (
<div>
<ButtonComponent size="large">Do something</ButtonComponent>
</div>
)
}
In this case, Buttons are defined in this same library, but on the side of the application where the library is consumed, the buttons are modified, variants are added, some properties are added that are not part of the original component of the library. And I am telling the component to use a different component like this:
<container component={FancyButton} />
As I said this works, but it feels like there might maybe be a more elegant solution to this. This is relevant, because the library uses an atomic design methodology approach, where some of the more complex components use less complex components, but they are being modified for a specific usecase. So all the buttons are being modified to have additional variants, etc. But if I then go a head and for example use the modal, it uses the regular buttons, not the modified buttons. This is the solution that I came up with, that allows me to tell the component to use these modified buttons instead.
Does this make sense? Is this an anti-pattern? Is there a more efficient/elegant solution to this?
€1: Here's a codesandbox demonstrating what this does: https://codesandbox.io/s/practical-ives-jxk3v
const defaultComponents = {
button: BaseButton
}
export const Card = ({ components=defaultComponents, children }) => {
return (
<div className="card">
<h2>Thing</h2>
{children}
<components.button className="card__button">Click me</components.button>
</div>
);
};
I don't know is this help. I will do like this.
use = when you passing props in a function mean the default value of that prop.
Here is a simple example:
<script>
import Button from './Button.svelte';
let text = 'Click me!';
let sayHello = () => alert('Hello!');
</script>
<Button {text} {sayHello}/>
<Button {text} {sayHello}/>
<Button {text} {sayHello}/>
And if I get it right, since there can be lots of <Button {text} {sayHello}/>, it'll be nice to omit props passing somehow
And here comes Context API:
<script>
import Button from './Button.svelte';
import { setContext } from 'svelte';
import { text, sayHello } from './data.js';
setContext(text, 'Click me!');
setContext(sayHello, () => alert('Hello!'));
</script>
<Button/>
<Button/>
<Button/>
And somewhere in ./Button.svelte there are getContext() usage, etc
So, is the ability to omit similar props passing is the only reason to use Svelte's Context API?
So, is the ability to omit similar props passing is the only reason to use Svelte's Context API?
No, and, in my opinion, it is even not a very good usage of context.
The problem here is that you're obfuscating the data relationship between your parent component and its button children.
With props, it is explicit what data is needed by the button and where it comes from. On the other hand, with context, you only see one side of the relationship at once. In the parent, you don't see how the data is used (or even if it is still used at all). Same in the child, you don't see where it comes from.
Also, mistyping a prop, or removing one that is still needed for example, will result in an instantly visible dev warning (replete with the exact location of the problem). With context, you might end up with an undefined value that will produce weird runtime behaviour but will be hard to track down.
So, while saving a little bit of typing might seem like a good idea when you're in the process of coding and have everything fresh in your head, it actually increases the complexity of your code and might play tricks on you and give you a big headache later down the road... Not a good trade off if you want my opinion.
There are situations, however, where props are not an option. That is, when the data consumer component is not a direct child of the data provider component.
For example, you might have some kind of user session in your app. It will most likely be stored in a component near the root of your components tree (say, App), but it will be needed in components several levels of nesting deeper. For example, in a component displaying the user's name. Or somewhere else in a page, displaying some parts based on whether the user is authenticated or not.
You could pass by props through every components down the line, but this is kind of insane. This would tie all the intermediate components to data they're absolutely not concerned with.
So, in a case like this, context makes full sense. You would setContext in the App component, and can access it from just the components that need it.
Another example would be some kind of "composite" component, where you have a wrapping component (for example a form) and multiple components that can be used inside of it (for example inputs) and that depends on some settings in the container.
<Form>
<Input />
</Form>
Here, the Form component can't pass props to the Input component because the Input is not created directly in the Form component. It is fed to it by mean of a slot, and the Form can't see the content of this slot.
Still, Input is nested under Form in the resulting component tree, and so you can pass data between them through context.
To sum it up, context is really meant for situations where you can't use props. Either because it would be impracticable and lead to bad architecture, or because it is technically impossible (slots).
As an alternative to context, you could store the data in a dedicated JS module that both the provider and the consumer would access (e.g. import { setData, getData } from './data-source.js') BUT that would make your components singletons. This data could only be global. With context, on the other hand, you could have as many isolated data "scopes" as you need, one for each instance of the data provider component. In the Form example above, multiple <Form> components could coexist in your app at the same time, each having their own data in context. (They could even be nested inside each other and it would work.)
To conclude, here's a piece of advice. Context in Svelte is implemented with JS Map object, so you don't have to use raw strings as context keys. I generally use plain objects (or Symbol if you want to be fancy) that I export from something like a constants.js module. This largely mitigates the mistyping and IDE confusion issues I mentioned earlier.
constants.js
export const key = {name: 'my-context'}
Form.svelte
<script>
import { setContext } from 'svelte'
import { key } from './constants.js'
setContext(key, { ... })
</script>
<slot />
Input.svelte
<script>
import { getContext } from 'svelte'
import { key } from './constants.js'
const { ... } = getContext(key)
</script>
...
This eliminates any risk of context key collision you could have with a raw string. It turns mistyping back into a fail fast and crash noisily error (which is good). And it gives your IDE a far better clue as to what is happening in your code (an ES import can easily be parsed by dev tools, while strings are just random blobs to them), allowing it be far more helpful to you when you'll need to refactor that...
I started to study ReactJS and can't understand the purpose of components. for instance lets have a component:
var QuoteMaker = React.createClass({
render: function () {
return (
<blockquote>
<p>
The world is full of objects, more or less interesting; I do not wish to add any more.
</p>
<cite>
<a target="_blank"
href="#">
Douglas Huebler
</a>
</cite>
</blockquote>
);}});
why can't we just use
var thisVar = (
<blockquote>
<p>
The world is full of objects, more or less interesting; I do not wish to add any more.
</p>
<cite>
<a target="_blank"
href="#">
Douglas Huebler
</a>
</cite>
</blockquote>
)
instead of that component.
You could. That would be just fine.
What you couldn't do is have a list of those, built from dynamic data.
What if you wanted multiple quotes from multiple people?
What if you wanted a button that randomized which quote appeared?
What if you wanted a twitter feed, or a blog roll, or a comment section?
You don't necessarily need the createClass. That's a particularly old way of looking at things, and you should really be using a Babel build pipeline; full stop.
You can simply say:
const Quote = (props) => (
<blockquote>
<p>{ props.quote }</p>
<cite >{ props.author }</cite>
</blockquote>
);
ReactDOM.render(<Quote quote="..." author="..." />, rootEl);
Now you can happily build a randomizer, or a quote API, or a quote editor, or whatever. The component does what it should, and it leans on input from the outside world for what it shouldn't do.
React allow you to get a composant approach. So you can use as a new balise text (composant) in react. You can reuse this component, it will have the same behavior everywhere.
Functional components
That way that you have in example can be created function, or stateless components, where we can use props object, that can be passed from the parent object.
const Component = (props) => {
//access to props
}
Class component
Class components allows to store thier own state and use it to rerender the component, via this.setState() function:
class Component extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
//access to both state and props
}
Why we must use components instead of js objects? Because react has its own DOM - virtual DOM, where it listening to changes and rerendering your real DOM, you can reuse or extend this component anywhere and its also semanticly awesome.
Take a look to the documentation about React components.
There are lots of uses for components. These are few:
Allegedly, if your component has no logics or special use, just flat html, so can pass using component, although it is more encapsulated and looks more neat.
If now or in the future you'll want to use some logics for your component, so it can be handled in the component. That is the Object Oriented approach for using html block of code. If your'e coming from Angular, you can think of it as "directive", from .Net "User control". Well, not exactly, but the purpose is same.
Calls and uses to different libraries. It is a better practice not to require libraries as global variable, but only for your use in the component.
And of course the most important factor: You can take advantage of the react "digest loop". You can choose what to do on constructing, before rendering, after rendering, and much more.
You can look it up here: https://facebook.github.io/react/docs/react-component.html
As i said before, there are lots of uses for components over flat html, and basically this is the whole point of ReactJS as component based :)
Say in my first file, open.jsx, I have:
// Default Import Statements here
var open = React.createClass({
render() {
return (
<div>
<Dialog
title="Test"
ref="openDialog">
</Dialog>
</div>
);
},
_handleTouchTap() {
this.refs.openDialog.setState({open:true});
}
});
module.exports = open;
And in my app.jsx file I have:
const testComponent = React.createClass({
render() {
return (
<FlatButton label="Test" onTouchTap={this._handleTouchTap}/>
);
},
_handleTouchTap: function() {
Open._handleTouchTap();
}
});
module.exports = testComponent;
The error I am getting currently is:
Uncaught TypeError: Open._handleTouchTap is not a function
Does anyone know how I can pass methods in between files for React?
I want to call open.jsx's _handleTouchTap() method when they press the button in app.jsx.
When you call Open._handleTouchTap, you are attempting to call the method as if it was static, on the Open class. This method, however, is only available once an Open component has been instantiated. You must attach a ref to the component and call the method via this.refs.someOpenComponent._handleTouchTap().
You may want to provide more of your code so better examples can be provided.
Also, methods with an underscore in front of their names typically denote "private" methods, and should not be called from a different class. You may want to consider renaming this function so it is more clear what its purpose is.
I'm assuming you want to render some page with a button, and show the dialog as soon as someone presses the FlatButton. I also notice you're using material-ui, so let's go with that.
When starting any React project, it's a good idea to think about your component hierarchy. Because you're using material-ui and the Dialog component's opening is controlled by passing props, it's easiest to use the following approach.
Simple case
Use a root component App (in app.jsx), which mounts a button and mounts a dialog, but the dialog is initially in a hidden state (the "open" prop on Dialog defaults to false) so doesn't visually show up yet (even though it is mounted). In this case, you will want the button to set the open prop on Dialog to true as soon as the button is pressed.
Please note I would recommend separating most of this rendering stuff into separate components; for illustration purposes, let's keep everything in App.jsx.
The way you want to organise in this case is as follows:
// App.jsx (render + handle click function only)
render: function() {
return (
<div>
<FlatButton label="Test" onTouchTap={this._handleTapTestButton}/>
<Dialog
title="Test"
open={this.state.dialogOpen}>
<div>My dialog contents</div>
</Dialog>
</div>
);
},
_handleTapTestButton: function() {
this.setState({dialogOpen: !this.state.dialogOpen}); // flip the state
}
See? No refs needed even (and that's good!). Now, this works fine if your Dialog component is located nice and close to your FlatButton.
More complex case: Dialog is far away from FlatButton
Your next question might be "how can I organise this when the Dialog component is nested somewhere deep inside a totally different component that is not a child or parent of the App.jsx component", but instead a sibling?
Well, this smells a little to me (just an opinion). It's not an anti-pattern per sé, but if you can avoid this, I would recommend you do. Ie: for your own convenience and for maintainability's sake, try to keep components that naturally interact with each other close (in terms of parent-child) to each other in the component hierarchy. This way, you can communicate pretty easily using props (see React's info on this. That's definitely not an absolute rule though, there are plenty of reasons to deviate from that.
Let's assume you have a valid case for not doing that, and even worse: the component are siblings, not direct or indirect grandparent/parent/child.
In that case, you have two options:
Use a store and associated events (or any other javascript code that communicates state) to communicate the state change to the other component (ie using Flux, Redux, or whatever you prefer). In this case, when the button is clicked, you fire an event somewhere that gets picked up by the other component. This event triggers a state change in the other component. Warning: this can get unmanageable pretty quickly, which is one of the reasons state-managing-frameworks like Flux and Redux exist.
OR, onTouchTap, have the FlatButton call a function that was passed down from a shared parent component. This function then flips the state at the shared parent component, and passes this state as props to the Dialog. Ie, when both components share a grandparent somewhere, you can define a function at the grandparent level and pass that function as a prop down to the FlatButton. The function's role is to change the state at the grandparent (dialogOpen). This state is then passed down one or more components as a prop all the way down the hierarchy until it ends up at the Dialog, which will auto show itself as the prop switches to true.
There are serious advantages/disadvantages to either approach. The first approach leaks your UI rendering logic into your stores (which is usually inevitable anyway, but can be managed using things like Flux), the second leaks it into the component hierarchy (tricky for maintainability) and tends to create tight coupling (yuck).