Convert Back to ES6 Classes in JavaScript - javascript

I'm struggling what the equivalent syntax is for changing the following code to the equivalent ES6 syntax.
export default class EventSessionItem extends Component {
state = {
isDetailsVisible: false,
};
revealDetails = () => {
this.setState({isDetailsVisible: true});
};
I would have thought that the below would be the conversion but clearly not. It all lights up red as bad syntax.
export default class EventSessionItem extends Component {
constructor() {
isDetailsVisible = false;
}
function revealDetails {
this.setState({isDetailsVisible: true});
};

Your code with the class initialisers desugars to property assignments in the constructor:
export default class EventSessionItem extends Component {
constructor(...args) {
//^^^^^^^^^^^
super(...args);
this.state = {
// ^^^^^
isDetailsVisible: false,
};
this.revealDetails = () => {
// ^^^^^
this.setState({isDetailsVisible: true});
};
}
}

I think this is what you want. Assuming you are using React.
export default class EventSessionItem extends Component {
constructor(props) {
super(props);
this.state = {
isDetailsVisible: false
}
this.revealDetails = this.revealDetails.bind(this);
}
revealDetails() {
this.setState({isDetailsVisible: true});
}
}
The two class fields that you are using in the first example state = and revealDetails = () => are not part of the standard yet. https://github.com/tc39/proposal-class-fields

Follow this approach with some fixes from yours:
class Component {}
class EventSessionItem extends Component {
constructor() {
super();
this.isDetailsVisible = false;
}
setState(newState) {
this.isDetailsVisible = newState;
}
// This is to illustrate!
getState() {
return this.isDetailsVisible;
}
revealDetails() {
this.setState({
isDetailsVisible: true
});
};
}
var esi = new EventSessionItem();
esi.revealDetails();
console.log(esi.getState());
.as-console-wrapper { max-height: 100% !important; top: 0; }

Related

How to inject functions that change component state?

I want to keep some functions outside of my component for easier testing. However, I cannot change state with these functions because they cannot reference the component's state directly.
So I currently have the hacky solution where I set the function to a variable then call this.setState. Is there a better convention/more efficient way to do this?
Example function code in Tester.js:
const tester = () => {
return 'new data';
}
export default tester;
Example component code in App.js (without imports):
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
let newData = tester();
this.setState({ data: newData })
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
You could bind your tester function like this (this approach doesn't work with arrow functions):
function tester() {
this.setState({ data: 'new Data' });
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
this.tester = tester.bind(this);
}
componentDidMount() {
this.tester();
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
But I would prefer a cleaner approach, where you don't need your function to access this (also works with arrow functions):
function tester(prevState, props) {
return {
...prevState,
data: 'new Data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: '',
};
}
componentDidMount() {
this.setState(tester);
}
render() {
return (
<div>{this.state.data}</div>
);
}
}
You can pass a function to setState() that will return a new object representing the new state of your component. So you could do this:
const tester = (previousState, props) => {
return {
...previousState,
data: 'new data',
};
}
class App extends Component {
constructor() {
super();
this.state = {
data: ''
}
}
componentDidMount(){
this.setState(tester)
}
render() {
return(
<div>{this.state.data}</div>
)
}
}
The reason being that you now have access to your component's previous state and props in your tester function.
If you just need access to unchanging static placeholder values inside of your app, for example Lorem Ipsum or something else, then just export your data as a JSON object and use it like that:
// testData.js
export const testData = {
foo: "bar",
baz: 7,
};
...
// In your app.jsx file
import testData from "./testData.js";
const qux = testData.foo; // "bar"
etc.

How to access and extend ES7 parent class attributes at declare time in sub-class body?

Considering this class
class BasePage extends Component {
state = {
locale: 'en'
};
render() {
return (<div>{ this.state.locale }</div>);
}
}
How can I declare a sub-class that will also declare a state attribute that will not override the parent class attribute, but will extend it?
class FooPage extends BasePage {
state = Object.assign( ?super.state, {
foo: 'Hello'
});
render() {
return (<div>{ this.state.locale } : { this.state.foo }</div>);
}
}
Obviously, super.state does not work, and BasePage.prototype.state does not exist either.
Is this even possible?
I'm going to answer it from a plain JS perspective (but same concept applies to React if you're using React).
In order for you to use base's props, you'd need to do so in the child's constructor:
class BasePage {
state = {
locale: 'en'
};
}
class FooPage extends BasePage {
constructor() {
super(); // can't access this props without calling super first
// this.state refers to super's state prop. We simply extend it using Object.assign.
this.state = Object.assign(this.state, {
foo: 'Hello'
});
}
render() {
const { locale, foo } = this.state;
console.log({ locale, foo }); // prints { locale: 'en', foo: 'Hello' }
}
}
Demo: https://repl.it/#mrchief/UrbanLiveTrace
Note about React: Since React is built on top of plain JS, the same concepts (i.e. calling super first applies there as well).
Alternatively, if you don't like the constructor approach, you can achieve similar effect via getters:
class BasePage {
get state() {
return { locale: 'en' }
}
}
class FooPage extends BasePage {
get state() {
return Object.assign(super.state, {
foo: 'Hello'
})
}
render() {
const { locale, foo } = this.state;
console.log({ locale, foo }); // prints { locale: 'en', foo: 'Hello' }
}
}
Demo: https://repl.it/#mrchief/GloriousGainsboroOpensoundsystem

Migrating from React ES6 to TypeScript: converting (This) to type

I am fairly new to TypeScript, and have been handed an ES6 based react project, and would like to Migrate over to TypeScript for a more integrated state. Anything following a (this.) seems to break with error (Property "foo" does not exist on Type "bar")
import * as React from 'react';
import { Component } from 'react';
class Clock extends Component {
constructor(props: {}) {
super(props);
this.state = {
date: new Date()
};
}
componentDidMount() {
this.timerId = setInterval(
() => this.tick(), 500
);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
const options = { hour24: false };
return (
<div className="clockDesign">{this.state.date.toLocaleString('en-US', options)}</div>
);
}
}
export default Clock;
My question is this. How would I go about converting the inline definitions to types within my class statement?
Use interfaces (https://www.typescriptlang.org/docs/handbook/interfaces.html):
interface Props = {
};
interface State = {
date: Date;
}
class Clock extends Component<Props, State> {
private timerId: any;
constructor(props: Props) {
super(props);
this.state = {
date: new Date()
};
}
...
}
Declare the fields of your class before assigning them. For example:
class Clock extends Component {
private state: { date: Date }
constructor(props: {}) {
super(props);
this.state = {
date: new Date()
};
}
// ...
}
Or if state is supposed to be inherited from Component, then you might have some problems with your typings (npm install #typings/react).
You need to add correct generic arguments and you need to define the class properties. See comments.
import * as React from 'react';
interface IProps { }
interface IState {
date: Date;
}
class Clock extends React.Component<IProps, IState> { //You need to define both props and state
constructor(props: {}) {
super(props);
this.state = {
date: new Date()
};
}
timerId: any; //Props needs to be defined;
componentDidMount() {
this.timerId = setInterval(
() => this.tick(), 500
);
}
componentWillUnmount() {
clearInterval(this.timerId);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
const options = { hour24: false };
return (
<div className="clockDesign"> {this.state.date.toLocaleString('en-US', options)} </div>
);
}
}
export default Clock;

Reset initial state in React + ES6

I have a class, ElementBuilder below, and when the user saves the Element they've built, I want the state to reset to the values below.
I have some functions in this class that I haven't provided but that change the state of title, size, and color.
In ES 5, I would have a getInitialState function on my class and could call this.getInitialState() in a function.
This element lives in my app for the lifecycle of a logged in user and I want the default values to always be the same regardless of past usage.
How do I achieve this without writing a function that sets an object of default values (or maybe that's the answer)? thanks!
class ElementBuilder extends Component {
constructor(props) {
super(props);
this.state = {
title: 'Testing,
size: 100,
color: '#4d96ce',
};
}
resetBuilder() {
this.setState({ this.getInitialState() });
}
}
You may use a getter function:
class ElementBuilder extends Component {
constructor(props) {
super(props);
this.state = this.initialState;
}
get initialState() {
return {
title: 'Testing',
size: 100,
color: '#4d96ce',
};
}
resetBuilder() {
this.setState(this.initialState);
}
}
or just a variable:
constructor(props) {
super(props);
this.initialState = {
title: 'Testing',
size: 100,
color: '#4d96ce',
};
this.state = this.initialState;
}
Using the proposed class fields, you could do something like this:
class ElementBuilder extends Component {
static initialState = {
title: 'Testing',
size: 100,
color: '#4d96ce'
}
constructor(props) {
super(props)
this.state = ElementBuilder.initialState
}
resetBuilder() {
this.setState(ElementBuilder.initialState)
}
}
Since the initial state doesn't seem to depend on anything instance specific, just define the value outside the class:
const initialState = {...};
class ElementBuilder extends Component {
constructor(props) {
super(props);
this.state = initialState;
}
resetBuilder() {
this.setState(initialState);
}
}
Use an High Order Component to clear component state (rerender)
Exemple Element.jsx :
// Target ----- //
class Element extends Component {
constructor(props){
super(props)
const {
initState = {}
} = props
this.state = {initState}
}
render() {
return (
<div className="element-x">
{...}
</div>
)
}
}
// Target Manager ----- //
class ElementMgr extends Component {
constructor(props){
super(props)
const {
hash = 0
} = props
this.state = {
hash, // hash is a post.id
load: false
}
}
render() {
const {load} = this.state
if (load) {
return (<div className="element-x"/>)
} else {
return (<Element {...this.props}/>)
}
}
componentWillReceiveProps(nextProps) {
const {hash = 0} = nextProps
if (hash !== this.state.hash) {
this.setState({load:true})
setTimeout(() => this.setState({
hash,
load:false
}),0)
}
}
}
export default ElementMgr

How to force rendering if the global variable value changes?

#File 1:
let ticketEnable = false;
export default class SupportTicketMain extends Component {
constructor () {
super();
}
render () {
let expandIcon = <DownIcon/>;
if (this.state.ticketDetailExpanded) {
expandIcon = <UpIcon/>;
}
return (
<Section className="ticketMain" primary={true}>
<TicketHeader expanded={ticketEnable}/>
</Section>
);
}
};
export function setTicketEnablement (value) {
ticketEnable = value;
}
#file 2:
import { setTicketEnablement } from file1;
export default class SupportTicketTabs extends Component {
constructor () {
super();
this.state = {
ticketDetailExpanded: false
};
this._expandClick = this._expandClick.bind(this);
}
_expandClick() {
this.setState({ticketDetailExpanded: !this.state.ticketDetailExpanded});
setTicketEnablement(this.state.ticketDetailExpanded);
}
render () {
let expandIcon = <DownIcon/>;
if (this.state.ticketDetailExpanded) {
expandIcon = <UpIcon/>;
}
return (
<Button className="expander" type="icon" onClick={this._expandClick}>
{expandIcon}
</Button>
);
}
};
Here a button click in supportTicketTabs class of #file2 will update global variable in #File1 , but SupportTicketMain render doesn't update if the global variable value changes! please guide me on this.
ticketEnable should be a prop passed into SupportTicketMain. The component that wraps both SupportTicketTabs and SupportTicketMain should be handing down a callback as a prop that modifies the value of ticketEnable (toggleTicketEnable) and the value of ticketEnable
class Main extends Component {
constructor (props) {
super(props);
this.onToggleTicketEnable = this.onToggleTicketEnable.bind(this);
this.state = {
ticketEnabled: false;
};
}
onToggleTicketEnable() {
this.setState({ ticketEnabled: !this.state.ticketEnabled });
}
render () {
return (
<App centered={false}>
<SupportTicketMain ticketEnable={this.ticketEnabled} />
<SupportTicketTabs onToggleTicketEnable={this.onToggleTicketEnable}/>
</App>
);
}
}

Categories