Translating React + Axios from Javascript > Typescript - reactjs

I'm trying to translate this highly useful codepen from Chris Coyier from JS to TS and running into some issues.
https://codepen.io/chriscoyier/pen/jqyWXo
Early days with Typescript and not sure what Class extend declaration to use.
I'm getting a Property or signature expected.ts(1131) on "const th = this" below.
Not sure whether it's the way I've defined the Class extend for React declaration because typically in TS declaring this const would work without the extend React call.
interface Props {
}
interface State {
}
class App extends React.Component<Props, State>{
function1 : () => { }
function2 : () => {
const th = this;
this.serverRequest =
axios.get(this.props.source).then(function(result) {
th.setState({ jobs: result.data.jobs});
})
}
}

function1 : () => { } syntax is valid for object literals, not for classes. In case it's an arrow, it should be TypeScript public property, also known as JavaScript class field.
const th = this recipe is obsolete when arrows can be used instead.
It should be:
class App extends React.Component<Props, State>{
function1 = () => { }
function2 = () => {
this.serverRequest = axios.get(this.props.source).then((result) => {
this.setState({ jobs: result.data.jobs});
})
}
...
}

Related

MobX - Reaction inside class component

Today I started using MobX and the first problem I ran into is how to execute a function in a React class component whenever an Observable updates.
I am under the impression this can be achieved using a reaction, but I'm not sure how to make it work.
class MissionLog {
private _missions: Array<IMissionItem> = [];
public get missions() {
return this._missions;
}
constructor() {
makeAutoObservable(this);
}
// Example of a method that modifies the _missions array
public receiveMission(mission: IMissionItem) {
this._missions.push(mission);
}
}
export const missionLog = new MissionLog();
// Example of modifying the missions array
missionLog.receiveMission(someMission);
export const ObserverTest = observer(class _ObserverTest extends React.Component {
constructor(props: any) {
super(props);
// Executes the console.log at the start,
// but not when missionLog.missions changes.
autorun(() => {
console.log("Autorun", missionLog.missions);
})
// Never executes the console.log
reaction(
() => missionLog.missions,
(mission) => {
console.log("Reaction");
}
)
}
render() {
return (
// Accessing missionLog.missions here
// gives me the correct, updated data,
// so my setup should be fine.
)
}
});
I also tried to use intercept and observe instead of reaction, but also no result.

React data undefined in component after callback from class object

I have a functional component and a class object which handles some stuff.
That class, created in a component does some calculations, then calls a callback function received from component and stored as a reference in the class object.
I don't think this is a proper way of approaching this issue but I dont really have a clue on how to do this different.
I believe that its an issue of where the function was called from but thats about it.
The class:
export const myClass {
private static instance: myClass;
callbackRef:() => void;
private constructor() {
callbackRef = () : void => console.log('unset Callback');
}
static getInstance(): myClass {
if (!myClass.instance) {
myClass.instance = new myClass();
}
return myClass.instance;
}
setCallback(callbackFn: () => void) : void {
this.callbackRef = callbackFn;
}
doSth() {
this.callbackRef();
}
}
The component:
import React, { useEffect, useState } from 'react'
const MyComponent = () : JSX.Element => {
const [someState, setSomeState] = useState<boolean>(false);
const someCallback = () : void => {
console.log(someState)
}
const myclass = myClass.getInstance();
myclass.setCallback(someCallback);
myclass.doSth();
useEffect(() => {
console.log(someState);
}, []);
return(<></>);
}
console output will be
false undefined
To my understanding if MyComponent was a class component I could simply this.someCallback.bind(this)
however it's a functional component and this does not exist.
I've also tried using
const someCallback = useCallback(() : void => {
console.log(someState);
}, []);
and passing it to class instance, to no avail.
Ay help would be highly appreciated, thank you D: !
(Tips on how to handle this kind of calculations aswell ;_;)

Convert "enquireScreen" js written for jsx to typescript?

Currently, I am trying to learn typescript through ionic react by creating simple webpages. I came across this interesting navigation bar but it is written in jsx. Hence, I want to write in tsx with the help of this website.
Code in jsx
import { enquireScreen } from 'enquire-js';
let isMobile;
enquireScreen((b) => {
isMobile = b;
});
class Home extends React.PureComponent {
state = {
isMobile,
}
componentDidMount() {
enquireScreen((b) => {
this.setState({
isMobile: !!b,
});
});
}
How do I write this in typescript? I have tried changing stuff like the declaration of the variable but could not get it to work. Thanks.
All you really have to do here is add types to your variables.
let isMobile: boolean;
enquireScreen((b: boolean) => {
isMobile = b;
});
export class Home extends React.PureComponent {
state = {
isMobile,
}
componentDidMount() {
enquireScreen((b: boolean) => {
this.setState({
isMobile: !!b,
});
});
}
}
Since the enquire-js doesn't come with type declarations and none are published to #types, you'll need to create a local declaration file and either write the declarations yourself, or declare the module with out any other declarations and it will suppress the can't find module errors.
This works for me.
Create a new file name enquire-js.d.ts (I just put in on the root of the project)
declare module 'enquire-js' {
var enquireJs: {
enquireScreen: {
cb: unknown,
query?: string
}
}
export = enquireJs
}
The warning goes away and compile without error.
(BTW, I am using enquire-js with next.js and antd)

What's different between two ways of defining React Component?

There're 2 ways to define a React component.
First one is like below.
class SomeComponent extends React.Component {
constructor (props) {
super(props)
this.state = {
someState: false
}
this._handleOnChangeState = this._handleOnChangeState.bind(this)
}
_handleOnChangeState (e) {
this.setState({ someState: e.target.value })
}
....
}
Second one is like below.
class SomeComponent extends React.Component {
state = {
someState: false
}
_handleOnChangeState = (e) => {
this.setState({ someState: e.target.value })
}
....
}
These two codes are the same function, but I guess there's some different something like memory usage or etc.
Can someone make it clearly? Thanks in advance!
This is a new proposal (class fields) for ES which is in stage 3 right now. To run a code written in this way you need a transpiler like Babel and an appropriate plugin.
Before transpile:
class A {
static color = "red";
counter = 0;
handleClick = () => {
this.counter++;
}
}
After transpile (with stage 2 on Babel Repl):
class A {
constructor() {
this.counter = 0;
this.handleClick = () => {
this.counter++;
};
}
}
A.color = "red";
In addition to the official proposal 2ality blog post is a good source to see what are the details.
Here is a reddit post if you have time to read the discussion storm what is the reason behind this proposal :)
The arrow function here is a different story. You can use instance properties without constructor and mix your code with standard functions. But when you want to use something like that this won't work:
class App extends React.Component {
state = { bar: "baz"}
foo() { console.log(this.state.bar) };
render() {
return <div><button onClick={this.foo}>Click</button></div>;
}
}
We need to bind our function in somehow like:
return <div><button onClick={this.foo.bind(this)}>Click</button></div>
But, binding our function in a JSX prop is no so good since it will create our function in each render.
One way to do this nicely bind in our constructor:
constructor(props) {
super(props);
this.foo = this.foo.bind( this );
}
But, if I have to write a constructor what is the point? This is why you see arrow functions everywhere where we define the classes like your second example. No need to bind to function thanks to arrow functions. But it is not directly related to this new proposal I think.
The first one is the traditional approach and the second one is when you babel-transform-class-properties plugin.
In the second type babel does the same thing under the hood, therefore it is a matter of convenience.

Should one use const when declaring an arrow function in React class

Inside a react class component, should one use a const/let to declare an arrow function, or they should be emmited:
class ReactComp extend Component {
const sayHello = () => {
return 'Hello';
}
sayBye = () => {
return 'Hello';
}
render() {
<div>
{this.sayHello}
{this.sayBye}
</div>
}
}
In this example, is sayBye declared correctly? (Without a const)
In addition, why outside the class, such a declaration does not work?
class ReactComp extend Component {
render() {
<div>
{sayHello}
{sayBye}
</div>
}
}
const sayHello = () => {
return 'Hello';
}
sayBye = () => {
return 'Hello';
}
This will return an exception: Uncaught ReferenceError: sayBye is not defined
Thanks a lot!
The answer is "it depends"... your two examples do very different things. Let's take a look at both before I give you a more detailed answer.
class ReactComp extend Component {
const sayHello = () => {
return 'Hello';
}
sayBye = () => {
return 'Hello';
}
render() {
<div>
{this.sayHello}
{this.sayBye}
</div>
}
}
The code above probably throws a syntax error as const (in this context) is not a valid decorator. Even if it was valid (or you simply omit it), sayHello() becomes a method on the ReactComp class (i.e. an instance method). Every time you create a new instance of this component, it will have an internal method called sayHello.
const example = <ReactComp />;
example.sayHello(); // oversimplified example
Make sense? On to your next example:
class ReactComp extend Component {
render() {
<div>
{sayHello}
{sayBye}
</div>
}
}
const sayHello = () => {
return 'Hello';
}
sayBye = () => {
return 'Hello';
}
Ignoring for a moment the syntax error you mentioned earlier, this code creates two global(ish) functions: sayHello() and sayBye() which (depending on your other code) could be accessed globally by any other component or script.
sayHello(); // I can run this line of code anywhere!
// no need for "const example = <ReactComp /> because sayHello() was defined outside of that class
My point: instance methods on a class are different than functions declared outside of a component.
Should one use const when declaring an arrow function in React class?
If you're creating an instance method, then no you don't need const. If you're creating a generic (i.e. utility) function outside of a component, then yes you probably should use const.
You can't define a variable using any declarative statement inside a class.
It expects property names to be attached to the this context of your class.
Defining the following class:
class C extends Component {
sayGoodBye = () => console.log("Bye!")
sayHello = who => console.log("Hello " + who)
render() {
this.sayGoodBye()
this.sayHello('world')
// ...
}
}
can be translated as:
const C = {
sayGoodBye : () => console.log('bye!'),
sayHello : who => console.log('Hello ' + who),
render : () => {
C.sayGoodBye()
C.sayHello('world')
}
}
if you try to define a variable inside a class using const/let/var it will result in an error.

Resources