Why set a React Component's state outside of the constructor? - javascript

So I just downloaded source code from a React framework, and I'm getting this error in Terminal:
ERROR in ./src/components/TextEditor.js
Module build failed: SyntaxError: Unexpected token (24:8)
22 |
23 | // Set the initial state when the app is first constructed.
> 24 | state = {
| ^
25 | state: initialState
26 | }
27 |
My question is, why do people set a React Component's state like this? What's the benefit if it'll error for some people? Also, is there a Babel preset or plugin I can get to prevent this error?
This is how I usually set a component's state, and from what I've seen, this is conventional:
constructor() {
super();
this.state = {
state: initialState
};
}
For the record, this is the entire document:
// Import React!
import React from 'react'
import {Editor, Raw} from 'slate'
const initialState = Raw.deserialize({
nodes: [
{
kind: 'block',
type: 'paragraph',
nodes: [
{
kind: 'text',
text: 'A line of text in a paragraph.'
}
]
}
]
}, { terse: true })
// Define our app...
export default class TextEditor extends React.Component {
// Set the initial state when the app is first constructed.
state = {
state: initialState
}
// On change, update the app's React state with the new editor state.
render() {
return (
<Editor
state={this.state.state}
onChange={state => this.setState({ state })}
/>
)
}
}

The first example is using class properties which is not part of the ES6 spec. You can use them with babel using the stage-2 preset or the babel-plugin-transform-class-properties plugin module.
The usage is mostly a matter of preference and will produce the same result as your second example when transpiling with babel.
https://babeljs.io/repl/#?evaluate=true&lineWrap=false&presets=react%2Cstage-0&experimental=false&loose=false&spec=false&code=%2F%2F%20Code%20goes%20here%0Aclass%20First%20%7B%0A%20%20state%20%3D%20%7B%0A%20%20%20%20value%3A%20true%0A%20%20%7D%0A%7D%3B%0A%0Aclass%20Second%20%7B%0A%20%20constructor%20()%20%7B%0A%20%20%20%20this.state%20%3D%20%7B%0A%20%20%20%20%20%20value%3A%20true%0A%20%20%20%20%7D%3B%0A%20%20%7D%0A%7D%3B%0A

Related

Importing anything from '#storybook/angular' cause error "Interface 'NodeRequire' cannot simultaneously extend types 'Require' and 'WebpackRequire'"

I configured storybook in Angular app and tried running following first story using storybook demo components.
import { Button } from '#storybook/angular/demo';
export default { title: 'My Button' };
export const withText = () => ({
component: Button,
props: {
text: 'Hello Button',
},
});
export const withEmoji = () => ({
component: Button,
props: {
text: '😀 😎 👍 💯',
},
});
This above example works perfectly fine. Now when I import anything from #storybook/angular like this
import { moduleMetadata } from '#storybook/angular';
// OR
import { storiesOf } from '#storybook/angular';
Build breaks with following errors
ERROR in node_modules/#types/webpack-env/index.d.ts(281,11):
TS2320: Interface 'NodeRequire' cannot simultaneously extend types 'Require' and 'WebpackRequire'.
Named property 'context' of types 'Require' and 'WebpackRequire' are not identical.
ERROR in node_modules/#types/webpack-env/index.d.ts(281,11):
TS2320: Interface 'NodeRequire' cannot simultaneously extend types 'Require' and 'WebpackRequire'.
Named property 'ensure' of types 'Require' and 'WebpackRequire' are not identical.
ERROR in node_modules/#types/webpack-env/index.d.ts(354,11):
TS2320: Interface 'NodeModule' cannot simultaneously extend types 'Module' and 'WebpackModule'.
Named property 'hot' of types 'Module' and 'WebpackModule' are not identical.
I tried multiple solutions but none of them worked.
Update:
I was able temporarily fix it to move forward by modifying the interfaces in node_modules/#types/webpack-env/index.d.ts
// this line
interface NodeModule extends NodeJS.Module {}
// replaced with
interface NodeModule {}
Similarly
interface NodeRequire extends NodeJS.Require {}
// replaced with
interface NodeRequire {}
and finally
interface Module extends __WebpackModuleApi.Module {}
// replaced with
interface Module {}

Adding properties to created Redux Store in Typescript

I'm having issues adding a property when I create a new Redux Store in Typescript:
const bindMiddleware = middleware => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension')
return composeWithDevTools(applyMiddleware(...middleware))
}
return applyMiddleware(...middleware)
}
function configureStore (initialState = exampleInitialState) {
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
rootReducer,
initialState,
bindMiddleware([sagaMiddleware])
)
store.sagaTask = sagaMiddleware.run(rootSaga)
return store
}
export default configureStore
with the following error message:
27:9 Property 'sagaTask' does not exist on type 'Store<{ error: any; count: number; lastUpdate: number; light: boolean; placeholderData: any; } | { lastUpdate: any; light: boolean; count: number; error: boolean; placeholderData: any; }, any>'.
25 | )
26 |
> 27 | store.sagaTask = sagaMiddleware.run(rootSaga)
| ^
28 |
29 | return store
30 | }
Any suggestions?
#types/
| - redux.d.ts
src/
| ...
| ... store.ts
in redux.d.ts
import * as Redux from "redux"
declare module "redux" {
export interface Store {
sagaTask: ... // provide the types for `store.sagaTask` here
}
}
in store.ts
const store = createStore(...)
store.sagaTask = ...
this is known as declaration merging / module augmentation.
You need to also setup your tsconfig.json to read types from this #types dir. Add "typeRoots": [ "./#types", "./node_modules/#types" ] to this file (note: these are relative to your baseDir, so modify accordingly.
Also note that you're creating a contract between your types and the code you write. If you fail to setup your middleware properly, sagaTask will be Required in types but undefined in reality.
I haven't used Saga so I'm not sure about best practices or what ramifications this approach might have, but the issue is that the Store type doesn't have a sagaTask, so TypeScript won't let this happen. The easiest way that I can see to solve this is to extend Store and add a sagaTask field, then copy the old store to a new object of the extended type.
The downside to this is that you could easily run into a situation where a function is expecting an object with a Store type, so it won't accept this new object.
Are you sure this needs to be a property of the store, or can you just run sagaMiddleware.run(rootSaga)?

Failing Jest unit testing

I have a component in which I wrote test for. It worked great but now something is going wrong and I cannot figure out what.
This is the simple component which takes two numbers and returns their sum:
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/css/bootstrap-theme.css';
import React from 'react';
import ReactDOM from 'react-dom';
import { LogOutButton } from './LogOutButton.js';
class Home extends React.Component {
displayName = Home.name;
state = {
result: 0,
val1: 0,
val2: 0,
};
handleChangeOne = event => {
this.setState({ val1: event.target.value });
};
handleChangeTwo = event => {
this.setState({ val2: event.target.value });
};
add = () => {
this.setState({
result: parseInt(this.state.val1) + parseInt(this.state.val2)
});
};
onLogoutClick = () => {
window.location.href = 'https://www.MICROSOFT.com';
}
render() {
return (
<div>
<h1>Hello world! The result is: {this.state.result}</h1>
<input type="text" onChange={this.handleChangeOne} />
+
<input type="text" onChange={this.handleChangeTwo} />
= <br />
<button onClick={this.add}>Add</button>
<br/><br/>
<LogOutButton onLogout={this.onLogoutClick} />
</div>
);
}
}
export default Home;
And this is the test which used to work great:
import React from 'react';
import { shallow } from 'enzyme'
import ReactDOM from 'react-dom';
import App from './App';
import { Home } from './components/Home';
describe('Home />', () => {
it('Renders a sum', () => {
const home = shallow(<Home />);
var first_value = home.state().val1;
var second_value = home.state().val2;
var result = first_value + second_value;
expect(result).toBe(0);
const inputs = home.find('input');
inputs.at(0).simulate('change', {target: {value: 5} } );
inputs.at(1).simulate('change', { target: { value: 8 } });
home.find('button').simulate('click');
home.update();
expect(home.state().result).toBe(13);
});
});
This is the error that I get:
FAIL src/Home.test.js
● Test suite failed to run
C:/Users/Itay/Documents/Experiments/WebApplication1/WebApplication1/ClientApp/src/components/Home.js: Unexpected token (8:12)
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html
Details:
6 |
7 | class Home extends React.Component {
> 8 | displayName = Home.name;
| ^
9 |
10 | state = {
11 | result: 0,
What's going on here? I have tried several things but nothing helped so far.
To make your test run I had to add this in package.json:
"enzyme-adapter-react-16": "1.6.0"
And this in the test:
import Enzyme, { shallow } from 'enzyme';
import Adapter from "enzyme-adapter-react-16";
Enzyme.configure({ adapter: new Adapter() });
The test then passed.
Note that I tested this in a CRA 2.1 environment. https://github.com/facebook/create-react-app
is your app compiling OK? do you have separate babel configs for app and tests?
i think Jest complains about the line utilizing field declarations syntax, which is stage-3 proposal right now.
Anyway, displayName is class property IIRC, so I think you should call it with static keyword, like this:
static displayName = Home.name;
Although I am not sure what this line is supposed to do, can you elaborate on that? you don't need to set displayName explicitly if you don't need it to be different than name inferred from class name

How to define state in react components?

I am quite new to React and wonder how this should work:
class App extends Component {
> 4 | state = {
| ^
5 | bands: [],
6 | concerts: []
7 | }
here the error message:
ERROR in ./src/App.js
Module build failed: SyntaxError: Unexpected token (4:8)
Edit (the whole component):
import React, { Component } from 'react'
class App extends Component {
state = {
bands: [],
concerts: []
}
render() {
return <div>hei</div>
}
}
export default App
Some solution to this?
If the code is really as shown, you're trying to use a language feature ("class fields") that isn't in the language yet, it's still a stage 3 proposal. You need to ensure whatever transpiler you're using handles transpiling that.
If you don't want to use class fields, define your state property in a constructor:
class App extends Component {
constructor(...args) {
super(...args);
this.state = {
bands: [],
concerts: []
};
}
render() {
return <div>hei</div>
}
}
One option would be to put the state inside your constructor:
class App extends Component {
constructor(props) {
super(props)
this.state = {
bands: [],
concerts: []
};
}
render() {
// Here you can access to this.state
return <div>hei</div>
}
}

Writing Components in React - MERN stack issues

I am trying to figure out how to write a Component in a MERN app.
This is my best effort, taking account of this advice on how to go about it?
import React from 'react';
import ReactDOM from 'react-dom';
import * as ReactBootstrap from 'react-bootstrap'
var GreeterMessage = require('GreeterMessage');
var GreeterForm = require('GreeterForm');
class Greeter extends React.Component {
getDefaultProps: function () {
return {
name: 'React',
message: 'This is the default message!'
};
},
getInitialState: function () {
return {
name: this.props.name,
message: this.props.message
};
},
handleNewData: function (updates) {
this.setState(updates);
},
render: function () {
var name = this.state.name;
var message = this.state.message;
return (
<div>
<GreeterMessage name={name} message={message}/>
<GreeterForm onNewData={this.handleNewData}/>
</div>
);
}
};
When I save this and run web pack in my terminal to check everything, I get this feedback:
ERROR in ./app/components/Greeter.jsx
Module build failed: SyntaxError: Unexpected token (9:19)
7 |
8 | class Greeter extends React.Component {
> 9 | getDefaultProps: function() {
| ^
10 | return {
11 | name: 'React',
12 | message: 'This is the default message!'
# ./app/app.jsx 19:14-32
I can't find any resources to help me figure out what a token is, let alone when they are expected or unexpected.
Can anyone see where I'm getting this wrong? I've just finished 5 separate udemy courses that are supposed to be an intro to react and MERN, and I can't get the first step to work.
It looks like your mixing the older React.createClass syntax with the latest ES6 class notation. You can't mix and match :)
You're also using both CommonJS and ES6 versions of importing code into your file. Although this won't break the code (unless you're using a setup that doesn't support import, I would advise dine consistently wouldn't go amiss.
Here is an example of an amended version of your code to use the ES6 syntax:
import React from 'react';
import ReactDOM from 'react-dom';
import * as ReactBootstrap from 'react-bootstrap'
import GreeterMessage from 'GreeterMessage');
import GreeterForm from 'GreeterForm');
// sets the default values for props
Greeter.defaultProps = {
name: 'React',
message: 'This is the default message!'
};
class Greeter extends React.Component {
constructor(){
super();
// sets the initial state
this.state = {
name: this.props.name,
message: this.props.message
};
// due to this not being bound intrinsically to event handlers,
// it's advisable to do it here so that the reference to
// this.setState works as expected:
this.handleNewData = this.handleNewData.bind(this);
}
handleNewData(updates) {
// `this` is not automatically bound to event handlers in ES6
// Ensure that it is bound by using `.bind` (see constructor)
// OR with ES6 arrow functions
this.setState(updates);
}
render() {
var name = this.state.name;
var message = this.state.message;
return (
<div>
<GreeterMessage name={name} message={message}/>
<GreeterForm onNewData={this.handleNewData}/>
</div>
);
}
};
Issue is you are mixing es5 and es6 way of writing the react component. I will suggest you to write in es6. Pasted useful links in the last, refer those links for more details.
Write it like this:
class Greeter extends React.Component {
constructor(){
super();
this.state = {
name: this.props.name,
message: this.props.message
}
this.handleNewData = this.handleNewData.bind(this);
}
handleNewData (updates) {
this.setState(updates);
}
render () {
var name = this.state.name;
var message = this.state.message;
return (
<div>
<GreeterMessage name={name} message={message}/>
<GreeterForm onNewData={this.handleNewData}/>
</div>
);
}
};
Greeter.defaultProps = {
name: 'React',
message: 'This is the default message!'
};
Links:
DefaultProps
es6 class
React DOC
No Autobinding of methods

Categories

Resources