React alternative components definition syntax - javascript

I am the kind of person that do not see positively the "class" keyword in the Javascript language as I feel that it does not fit well with the underlying object model. To create objects, I also prefer factories over "new" and constructor functions.
Besides, I really like React. So I have come up with a way of defining components that completely avoids the whole "class" and "extends" thing and only uses functional components or component factories. It also completely avoids to have to bind class methods or to use tricky class properties defined as arrow functions (for event handlers defined as class methods).
Before describing it, here is my question: is it future-proof to use this syntax, or will React one day force me to use a syntax that I do not approve?
Here is how I define components depending on the needs :
Components that only depend on their props are written as functional components (function that returns JSX):
const Comp = (props) => (
<p> Hey { props.name } </p>
);
Comp.propTypes = {
name: PropTypes.string.isRequired
};
Components for which one wants to use lifecycle hooks but that do not maintain any state are written as a function that creates and returns an object whose members are the props, the needed lifecycle methods and the render method:
const Comp = (props) => ({
props,
componentWillMount() {
this.props.doSomething();
},
render() {
return (
<p> Hey { this.props.name } </p>
);
}
});
Comp.propTypes = {
doSomething: PropTypes.function.isRequired,
name: PropTypes.string.isRequired
};
Components that need to maintain state are written as a function that creates and returns an object whose prototype is React.Component (to gain the setState function) and whose members are the props, the state, the needed lifecycle methods and the render method. Functions called by the interface are defined in this factory function closure and act on the component (mainly setState) directly using the component instance (no binding problem here):
const Comp = (props) => {
const comp = Object.create(React.Component.prototype);
const handleClick = () => {
comp.setState((prevState) => ({ value: prevState.value + 1 }));
};
return Object.assign(comp, {
props,
state: { value: 4 },
componentWillUpdate() {},
render() {
return (
<p> Hey { this.props.name } </p>
<p> Value: { this.state.value } </p>
<button onClick={ handleClick }> Increment </button>
);
}
});
};
Comp.propTypes = {
name: PropTypes.string.isRequired
};

Related

How can I convert this hook based code to class based code? is it possible?

How can I convert this hook-based code to class-based code? Does the code still work?
I'm using a react/ASP.net Core template. My project consists of several components that are siblings.
I'm trying to send a state from a component to another one.
import { useState } from "react";
//the change is reflected in the ImageEditor component
const ImageEditor = ({ yourState }) => (
<p>State in the ImageEditor = {yourState}</p>
);
//ImageTile changes the state through the setYourState method
const ImageTile = ({ yourState, setYourState }) => (
<button onClick={() => setYourState("World!")}>
Change State from the ImageTile
</button>
);
//App is the parent component and contains both image editor and tile
const App = () => {
//the state which holds the image ID is declared in the parent
const [imageId, setImageId] = useState("Hello");
return (
<div>
<ImageTile yourState={imageId} setYourState={setImageId} />
<ImageEditor yourState={imageId} />
</div>
);
};
export default App;
You can see the complete code on:
https://codesandbox.io/s/billowing-brook-9y9y5?file=/src/App.js:0-775
A parent passes it’s state to a child via props. The child is not allowed to change its parents state, if a child wants to change a parents state then the parent passes a callback to the child that the child can call to change the state. This is fundamental to reacts state management. A child does not need to know how a parent stores it’s state (class instance, hook instance or state library).
if your application uses a global state manager like redux, then global state is mapped to props and a store action can be called to update global state. In this case the child does not need to know who else is using the state because it’s global.
class Foo extends Component {
constructor (props) {
super(props);
this.state = { myState: 0 };
this.setMyState = this.setMyState.bind(this);
}
setMyState (value) {
this.setState({
myState: value
});
}
render () {
return (
<MyChildCompoent myStat={this.state.myState} setMyState={this.setMyState} />
);
}
}
you'll need to declare the state in the parent:
state = {
someKey: '',
};
And then in the parent, define some function to update it:
updateSomeKey = (newValue) => {
this.setState({ someKey: newValue });
}
And then pass both of these values as props to your sibling components:
render() {
return (
<div>
<Sib1 someKey={this.state.someKey} updateSomeKey={this.updateSomeKey} />
<Sib2 someKey={this.state.someKey} updateSomeKey={this.updateSomeKey} />
</div>
)
}
You shouldn't need to in order to update the 'shared' state. In the code above, the someKey state can be updated in either component by calling the updateSomeKey function that is also available as a prop.
If either component calls that function (this.props.updateSomeKey('hello!')) the updated state will propagate to both components.

Exposing the state of a react widget

I have made a react UI widget thats let's the user select a number of different times and dates. The user's current selection is stored in the state of a top level component, DateTimePicker. I then have a widget wrapper like so:
import ...
export default {
new: (args) => {
const store = {
reactElement: <DateTimePicker
startDate={args.startDate}
endDate={args.endDate}
/>
};
return {
getState: () => {
return store.reactElement.getState(); // DOESN'T WORK
},
render: (selector) => {
ReactDOM.render(store.reactElement, document.querySelector(selector));
}
};
}
};
I want to add a validation to make sure that at least X days/times are selected, but this validation needs to be implemented outside of the widget.
For this, I'll need someway of asking the widget of it 's state. i.e. what has the user selected? Although it seems like the state of the class is not part of the public api of a react component.
How can I acess the state, or is there another way I'm missing?
The solution to doing things imperatively from the parent to the child usually involves getting a ref to the child component. Something along these lines:
export default {
new: (args) => {
let myRef = React.createRef();
const store = {
reactElement: <DateTimePicker
ref={myRef}
startDate={args.startDate}
endDate={args.endDate}
/>
};
return {
getState: () => {
return myRef.current.getState();
},
render: (selector) => {
ReactDOM.render(store.reactElement, document.querySelector(selector));
}
};
}
};
With ref={myRef} added as a prop, whenever DateTimePicker gets mounted, it will assign a reference to the mounted component to myRef.current. You can then use that reference to interact directly with the most recently mounted component.

callback in nested state object

I am fairly new to JS and I am having a bit trouble in understanding how to properly implement the callback passed to setState in React, for a controlled input. The following code is what I have so far:
class App extends React.Component {
...
this.state = {
properties: {
width: '',
height: ''
}
this.handleChange = this.handleChange.bind(this); //edit 1
}
handleChange(e){
this.setState(() => ({ properties[e.target.name]: e.target.value })) //edit 2
}
render(){
return(
<input
type="text"
value={this.state.properties.width}
name="width"
onChange={this.handleChange} />
...
)
}
}
https://codepen.io/anon/pen/YYQgNv?editors=0010
You need to change handleChange declaration:
class App extends React.Component {
...
handleChange = (e) => {
this.setState({ properties[e.target.name]: e.target.value })
}
...
}
When you write handleChange = (e) => {...} it will bind this pointer of the function to your component so that you will be able to access setState as pointed out by #Li357, it doesn't not bind at all, on the contrary it creates a property of the class that is an arrow function that doesn't bind this, capturing the this value of the surrounding scope, the class.
Update:
It has been pointed out that using arrow functions as class properties is an experimental feature, so it is safer to use this.handleChange = this.handleChange.bind(this) in constructor of the component.
I got the example working with this code:
handleChange(event) {
const target = event.target;
this.setState((prevState) => ({
properties: {...prevState.properties, ...{ [target.name]: target.value } }
})
);
}
I am not entirely sure why it behaves the way it does, I am guessing it has to do with the fact that setState is async and react wraps events in its own SyntheticEvent which will be reused and all properties will be nullified after the event callback has been invoked (see react docs). So if you store the original reference to target outside of setState it will get scoped and used inside setState.
Here is a working example on codesandbox.
Update 2:
According to react docs, one can't access react SyntheticEvent in an asynchronous way. One way of dealing with this would to be call event.persist() which will remove the wrapper, but this might not be a good idea since SyntheticEvent is a cross-browser wrapper around the browser’s native event which makes sure the events work identically across all browsers.

react without constructor declaring state in class's body

I saw below code somewhere and I'm curious. It looks cleaned but unusual to me. Why state = {} is declared without an constructor?
and load declared without a function keyword? As I know for there are ways to write a function
function async load() {}
or
const async load = ()=>{}
And what ...args does? is it spread arguments?
import View from './View';
import loadData from './loadData';
export default class extends Component {
state = {};
load = this.load.bind(this);
async load(...args) {
try {
this.setState({ loading: true, error: false });
const data = await loadData(...args);
this.setState({ loading: false, data });
} catch (ex) {
this.setState({ loading: false, error: true });
}
}
render() {
return (
<View {...this.props} {...this.state} onLoad={this.load} />
);
}
}
The state = {} declaration is a class property, which is currently not part of the JavaScript language. Certain utilities such as Babel will compile this into legal JavaScript code.
However, the lack of a function keyword within classes, as well as the ... operator are part of ECMAScript 6, which has been officially implemented into the language (though some browsers do not recognize it yet).
Class Definition
Spread Operator
Yes, you can initialize state without a constructor for a React class component:
class Counter extends Component {
state = { value: 0 };
handleIncrement = () => {
this.setState(prevState => ({
value: prevState.value + 1
}));
};
handleDecrement = () => {
this.setState(prevState => ({
value: prevState.value - 1
}));
};
render() {
return (
<div>
{this.state.value}
<button onClick={this.handleIncrement}>+</button>
<button onClick={this.handleDecrement}>-</button>
</div>
)
}
}
It uses class field declarations which are not part of the language yet but enabled with Babel. You can checkout a sample application over here.
You can also use state in React function components (without a constructor), but using higher-order components or render prop components. You can find out more about it here.

Correct way to inherit React components

I understand that my question is a little bit biased, but I am very new in Javascript and prototypes, and I read about it, but I don't really understand how to apply that techniques to my practical problems. So an example would be very helpful.
So I have a React component, that basically looks like that:
var Component1 = React.createClass({
getInitialState: function () {
return ({
searchable: true,
})
},
function1: function () {
return ({
somevalue
})
},
render: function () {
var redText = {
color: 'red'
};
var redBorder = {
color: 'red',
border: '1px solid red'
};
return (
<form>
<div>
<a onClick={this.props.handleAddClick}>Something</a>
</div>
<div>
<label>Some label</label>
<input type="text"/>
</div>
</form> )
});
I also have Component2 which is basically absolutely the same, but has one additional <input/> inside the return of its render function.
I also have Component3, which shares same functions, but has different render() function.
So how to apply inheritance here and avoid copy-paste 3 times? I just miss some practical illustration, so I'd appreciate it.
Edit1____________________________________________________
So I tried to implement Prototype inheritance as per the first answer, but it seems React doesn't see these functions: getInitialState() is null, initial state is null after rendering. What's wrong with this approach?
I also tried to go according to the textbook and did:
function MyPrototype() {};
MyPrototype.prototype.getInitialState = function () {
return ({
someProperty: true;
})
};
function Component1() {};
Component1.prototype = Object.create(MyPrototype.prototype);
Component1.prototype.render = function () {
console.log(this);
return (<div></div>)};
var MyComponent1 = React.createClass(new Component1());
But when I open my browser, I get an error: Uncaught Invariant Violation: createClass(...): Class specification must implement arendermethod.
What am I doing wrong this way?
Edit2_______________________________________________
Actually, I see that React doesn't support mixins neither prototypes. Composition should be used instead. It's explained in this article:
Dan Abramov's article Mixins Are Dead. Long Live Composition
In React, inheritance for components is severely discouraged.
React is much better suited for expressing the same relationships via composition.
Here is an example of using composition:
class Button extends Component {
render() {
return (
<div className='Button' style={{ color: this.props.color }}>
{this.props.children}
</div>
)
}
}
class DeleteButton extends Component {
render() {
return (
<Button color='red'>Delete</Button>
)
}
}
Note how DeleteButton uses the look and feel of Button without inheriting from it. Instead, Button defines its configurable parts via props, and DeleteButton supplies those props. In the actual DOM, both <Button /> and <DeleteButton /> would render to a single DOM node—the recursive resolution happens at the render() time, and this is the core idea of React.
In fact, if you don’t need lifecycle hooks or local state, you may even define components as functions:
function Button({ color, children }) {
return (
<div className='Button' style={{ color }}>
{children}
</div>
)
}
function DeleteButton() {
return (
<Button color='red'>Delete</Button>
)
}
You can even mix classes with functions. This would be impossible with inheritance, but works great with composition.
As for your specific use cases:
I also have Component2 which is basically absolutely the same, but has one additional <input/> inside the return of its render function.
You can have your Component1 accept this.props.children and use them in the return value of render() method, and have Component2 render to <Component1><input /></Component>. This is very similar to what I showed above. You also don’t have to use the children prop—you can pass a React element in any prop, e.g. <Component1 footer={<input />} />, and then you can use this.props.footer inside Component1.
I also have Component3, which shares same functions, but has different render() function.
If they share any other code (e.g. utilities that calculate some data), move that code outside components into a shared module, and import it from both components.
If they share any UI, extract it into yet another component, and use it from both your components.

Categories

Resources