I am using a component:- https://github.com/christianalfoni/formsy-react, for creating forms. I am trying to create one of my own components of the input. So as mentioned I need to use it for my's mixin. But unfortunately, there is no support for it in es6 style. So any work around anyone knows of.
here is my component code:-
import Formsy from 'formsy-react';
class DropDownAutoComplete extends React.Component {
constructor(props, context) {
super(props, context);
this.mixins = [Formsy.Mixin];
}
changeValue(event) {
this.mixins[0].setValue(event.currentTarget.value);
}
handleValue1Change(value) {
this.setState({value: value});
}
render() {
const className = this.mixins[0].showRequired() ? 'required' : this.mixins[0].showError() ? 'error' : null;
// An error message is returned ONLY if the component is invalid
// or the server has returned an error message
const errorMessage = this.mixins[0].getErrorMessage();
return <DropdownList
data={this.props.dropDownConfigs.value}
onChange={this.changeValue.bind(this)}
textField={this.props.dropDownConfigs.name}
caseSensitive={false}
filter='contains'>
</DropdownList>
}
}
It's throwing an error where the show required function is called. Apparently, its implementation uses some state variables like required.
By design, mixins do not work with ES6 classes - trying to hack something together is just going to cause you headaches!
One solution is to use what's called a higher-order component - a function that takes in a component, and returns a new component that wraps around it. These wrapper components can have lifecycle hooks of their own, and can pass props down to the wrapped components, effectively providing you with the same functionality mixins would give you, but arguably in a cleaner way!
formsy-react allows you to take this approach, providing its own HOC:
import {HOC} from 'formsy-react';
class MyInput extends React.Component {
render() {
return (
<div>
<input value={this.props.getValue()} onChange={(e) => this.props.setValue(e.target.value)}/>
</div>
);
}
};
export default HOC(MyInput);
You can use react-mixin-decorator.
Quoting from README:
If you're creating React components using ES6 classes and you'd like
to use existing mixins to add some nice functionality to your
component, you probably don't want to take the time to convert the
mixins to something that your ES6 React component class could use.
Related
I wrote a component Foo in React.js (its parameter "interface" consists of a single parameter text; there are more parameters in my real code):
import React from 'react';
export default class Foo extends React.Component {
constructor(props){
super(props);
}
render(){
return <div>{this.props.text}</div>;
}
}
and I thought I could improve the code by modifying the property access as:
import React from 'react';
export default class Foo extends React.Component {
constructor(props){
super(props)
this._text = props.text
}
render(){
return <div>{this._text}</div>;
}
}
This would give me the benefits that
I can immediately see what properties are supported by having a look at the constructor.
The code that applies the properties gets shorter/better to read.
However, this destroys the update workflow for the property. In a parent component I use Foo like
<Foo text={this.state.parentText}/>
and
this.setState({parentText: "new text"})
does not trigger an update of the text in Foo any more. The constructor of Foo is only called once and therefore, the private variable this._text is not updated on property changes.
=> Using extra private properties to modify the parameter access turned out to be a bad idea.
=> What would you recommend to have a clear interface for the component without breaking the update workflow?
Some ideas:
a) List all used properties at the start of render (and componentDidUpdate)
render(){
const text = this.props.text;
return <div>{text}</div>;
}
b) Create a getter for each property and put them directly under the constructor, for example
get _text(){
return this.props.text;
}
c) (Only for shorter access.) Try to avoid class components. With function components there is direct access with props.text instead of this.props.text. Or, as a workaround, inherit from a custom component class that passes props argument to render:
render_props(props){
...
}
(Why doesn't react pass this.props as an argument to render by default?)
d) Document the supported properties in a doc string
=> If you know a better option / some standard / best practice, please let me know.
Also tried but failed:
I tried to use state in the child component, with the hope that it would be automatically updated on updates of the parent state:
import React from 'react';
export default class Foo extends React.Component {
constructor(props){
super(props)
this.state = {
text: props.text
}
}
render(){
return <div>{this.state.text}</div>;
}
}
However, this also breaks the update workflow. Using this.state only seems to make sense in the parent component.
Related:
Can I update a component's props in React.js?
https://github.com/vasanthk/react-bits/blob/master/anti-patterns/01.props-in-initial-state.md
Related topic:
How to interact with third party libraries using function components?
https://reactjs.org/docs/integrating-with-other-libraries.html
How do I use/include third party libraries in react?
Integrating React with Other Libraries
Use function components for React >= 16.8, also see recommendation at
https://www.w3schools.com/react/react_class.asp
Use useState hooks instead of setState. This is the modern way to write React, and gives you a simpler way to access state (foo.text, foo.setText). https://reactjs.org/docs/hooks-state.html
Typescript would help with docs (type props = { text: string }), but I also would like the answer for d) (your question is several questions I think).
Use props.text directly, instead of using extra shortcut variable const text = props.text suggested by option a). This way, you don't have a list of all available properties on top of the component function. However, using a consistent props. prefix makes it easier to spot the injected variables in the react code. If there is a huge number of properties and its hard to identify them, try to improve modularization.
JavaScript example code:
Child component Foo:
import React from 'react';
export default function Foo(props){
return <div>{props.text}</div>;
}
}
Parent component:
import React, { useState } from 'react';
import Froo from './foo';
export default function Parent(){
const [parentText, setParentText] = useState('Hello world');
return <Foo text={parentText}/>;
}
ReactJS is a great library, However, it misses some features which I found in Vue and Angular. These features can be implemented of course in React, however, they require extra code to be written.
Every react component, or every JSX element I should say has the following properties shared, which are given by React to us to consume:
ref
key
I wanted to add extra props:
renderIf
fallback
These props help in a way I can't describe when it comes to conditional rendering and filtering the views based on the logged-in user permissions and roles (and other conditional rendering use cases, of course).
In react, if we wanted to apply these props to our components, we would use a HOC as follows:
// 🍎 Disclaimer: you don't have to understand any of the code written bellow, the general idea is that this is a HOC.
import React from 'react'
import getVal from './getVal'
export default function EnhancedComponent(OriginalComponent) {
return ({ renderIf: renderIf_ = true, override: override_, fallback: fallback_ = undefined, ...props }) => {
const renderIf = getVal(renderIf_)
const override = getVal(override_)
const fallback = getVal(fallback_)
const consumersComponent = <OriginalComponent {...props} />
let render = fallback
if (renderIf) render = consumersComponent
if (override_ !== undefined) render = override
return render
}
}
Where every time you want to apply these props to your components, you would have to wrap every new component you create with EnhancedComponent as follows:
export default EnhancedComponent(function Sidenav(){
return <div> side nav </div>
})
Now, you can use your Sidenav component within your App component as follows:
import Sidenav from './Sidenav'
export default function App(){
return (
<div>
<Sidenav renderIf={(5 + 5 === 10)}/>
<div>etc</div>
</div>
)
}
This API is great, but it has a drawback, which is, every time you want to apply these cool props (renderIf and fallback) you'll have to repeat these steps:
import Enhanced component to your file.
wrap your export with Enhanced component.
What I am looking for, is a method, or a way to inherit, or to add some props to the original react component class, somehow?
In react class components, I can imagine doing this on the React.Component class which we used to extend from in the past
class Car extends React.Component{
constructor(){}
render(){
return <div>I miss you 🌹</div>
}
}
But in react functional component, how can we do that?
I want to apply these props by default everytime I create a new component, without wrapping my components in a HOC everytime.
Does React have a way to do that? To change its defaults ?
#tl;dr
I want to extend a React Component without using HOC / Providers in Typescript
OK, so here's the deal...
At my work place we used to work with Vue and plain JS... then we decided to migrate to React with Typescript...
Tecnologies we use:
- React
- Typescript
- Redux
- Redux-Saga
The thing is, back in Vue, we could declare something like:
Vue.use(Auth)
and on every .vue file, inside the script tag we could call something like:
this.$auth
and have access to authorizatin methods.
What I want to do is... create an extension of ReactComponent where I already created some methods that most of my Component will use... something like:
auth // Check if user is authenticated, and if so, get the User Info
route // Give me the current route, with query params, redirects, etc...
Those were the only two I could think off here now.
I want to have in my .ts file something like this:
interface MyProps {
route: any // don't remember the correct Type
}
class MyComponent<T,S = {},SS = {}> extends React.Component<T,S,SS> {
$route = () => {
this.props.route
}
}
export default withRouter(MyComponent)
and have it being called in my application like this:
inteface AnotherProps {
}
class AnotherComponent extends MyComponent<AnotherProps> {
render() {
if(this.$route().location.pathname == "/") {
return <div>Root</div>
} else {
return <div>Not Root</div>
}
}
}
What I have tried so far
HOC (High Order Components)
I could achieve what I want using HOC, but the thing is... if possible, I would like 2 things.
To have this new properties being store at this and not this.props, and if that's possible using HOC, i don't know how
With HOC, I would also need to import the base Props, something like this:
import BaseProps from Outterspace;
inteface AnotherProps extends BaseProps{
}
and I want the logic inside the MyComponent and AnotherComponent to be as independent to each other as possible...
Providers
Same as HOC, I would need to pass the properties I want as props, and would need to extend my props interface.
[EDIT]
Decorators
Someone said in the comments that I could try using Decoratos, and while I did read the docs and it sounded promising... the last line of the Docs kinda worries me..
NOTE Decorator metadata is an experimental feature and may introduce breaking changes in future releases.
Thank you so much for reading this far ^^
If you are using typescript then you can create decorators.
You can call the decorator on top of your class and add the property.
#YourAuthDecorator({
'propertiesForconfiguration': 'value'
})
export class ReactClass extends ReactComponent {}
Example:
function abc() {
return {};
}
export function Auth() {
console.log("-- decorator function invoked --");
return function (constructor: Function){
console.log(constructor, 'called');
constructor.prototype.$auth = abc();
}
}
class Sample {
public prop = 'sample'
}
#Auth()
export class Content extends Sample {
}
export const a = new Content();
It will be the decorator's functionality to append various properties the to this instance providing you access to various functions/properties like auth
You can read more about decorators here.
https://www.typescriptlang.org/docs/handbook/decorators.html
I'm getting the following error on webpack build and I don't understand why:
SyntaxError: this is a reserved word (11:5)
It occurs inside the Applicatons class at the code which says this.props.apps.map. Its trying to iterate through the passed apps property and create a JSX representation of Application components. I've included the Applications class as the first piece of code and the subsequent code shows how I instantiate the Applications component in a different class. I'm trying to access the props field inside the Applications class which extends the React Component
Here is the Applications class which I am clearly not using React props correctly:
import React from 'react';
import ReactDOM from 'react-dom';
import Application from './Application/Application';
import ErrorBoundary from '../ErrorBoundary/ErrorBoundary';
class Applications extends React.Component {
render(){
let applicationsList=null;
applicationsList = (
{this.props.apps.map((app,index)=>{
return <Application
name={app.name}
desc={app.desc}
changed={(event)=>this.props.changed(event,app.id)}
click={()=>this.props.clicked(index)}
key={app.id}
/>
})}
);
return (
{applicationsList}
)
}
}
And here is the code inside a different react component that instantiates the Applications component.
render(){
let applications=null;
applications = (
<div>
<Applications
apps={this.state.apps}
clicked={this.deleteApplicationHandler}
changed={this.nameChangedHandler}/>
</div>
);
return (<div>{applications}</div>);
}
I'm extremely new to react so i apologize if i missed anything if i did please let me know and ill update the question.
You're trying to use JSX templating syntax outside JSX. Curly brackets are interpreted as defining an object literal. Remove the extra brackets.
render(){
let applicationsList=null;
applicationsList = this.props.apps.map((app,index)=>{
return <Application
name={app.name}
desc={app.desc}
changed={(event)=>this.props.changed(event,app.id)}
click={()=>this.props.clicked(index)}
key={app.id}
/>
});
return applicationsList;
}
For a simple component written like this:
import React from 'react'
const MyComponent = ({text}) => {
return(<div> {text} </div>
}
Is there a syntax for implementing lifecycle method like componentDidMount() or do I have to convert code to React.Component class?
Is there a syntax for implementing lifecycle method like componentDidMount()
No.
do I have to convert code to React.Component class?
Yes.
See https://facebook.github.io/react/docs/state-and-lifecycle.html
In the alpha version of React there is an opt in feature called hooks which will address this. See more on hooks here https://reactjs.org/docs/hooks-overview.html