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
Related
I'm creating a node module for React just to try myself out and learn more. But I'm facing a problem. I want to make this module without any dependencies (except React) but I also want to have I18n in my module. I have started out like with this file structure:
src
|- Components
| |- myComponent.jsx
|
|- I18n
| |- en-us.js
| |- sv-se.js
|
|- index.jsx
So I want to use the I18n js files depending on which language the user calls to the module. Here are my current files:
index.jsx:
import React from 'react';
import Component from './Components/component';
class MyNewModule extends React.Component {
constructor(props) {
super(props);
this.state = {
greetings: [],
};
}
componentDidMount() {
this.setState({ greetings: [
'Hola',
'Como estas?',
'Como te llamas?',
] }); // Will use props later so user can send to module
}
render() {
return (
<Component greetings={this.state.greetings} locale={this.props.locale} />
);
}
}
export default MyNewModule;
myComponent.jsx:
import React from 'react';
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
greetings: [],
};
this.getGreetings = this.getGreetings.bind(this);
}
componentDidMount() {
this.getGreetings();
}
getGreetings() {
let greetings;
if (props.greetings && props.greetings.length) {
greetings = props.greetings;
} else {
greetings = []; // HERE I WANT TO FETCH GREETINGS FROM FILE BY props.locale
}
this.setState({ greetings });
}
render() {
return (
<div>
{this.state.greetings.map((g, i) => {
return (
<div key={i}>
{g}
</div>
);
})}
</div>
);
}
}
export default MyComponent;
en-us.js:
function Lang() {
return {
greetings: [
'Hello',
'How are you?',
'What is your name?',
],
};
}
export default Lang;
sv-se.js
function Lang() {
return {
greetings: [
'Hej',
'Hur mår du?',
'Vad heter du?',
],
};
}
export default Lang;
As you can see I want to basically fetch the greetings from the corrent language file, depending on which locale the user use as property.
I have tried finding a good way to do this, and the only way I have found is to use require as stated in Dynamically import language json file for react / react-intl but I feel that there might be a better and more "reacty" way to do this.
Any suggestions?
EDIT 1:
After suggestion from #Ozan Manav I changed the language files to use
function Lang() {
greetings: [
'Hello',
'How are you?',
'What is your name?',
],
}
and call it by using:
greetings = require(`./I18n/${this.props.locale}.js`).Lang.greetings;
but I would still like to not have to use require if possible.
Why are you trying to return it as a function?
Maybe you can use as Greetings ["en-EN"] or Greetings ["sv-SE"]
export const Greetings = {
"en-EN": ["Hello", "How are you?", "What is your name?"],
"sv-SE": ["Hej", "Hur mår du?", "Vad heter du?"],
};
Could that be?
hey everyone i am observing this strange behavior in my react app created via CRA(create-react-app) where the console log messages i am trying to log are getting logged inconsistently and sometimes not at all here is my code
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
class Dynamic extends Component {
constructor(props) {
super(props);
this.state = { module: null };
}
componentDidMount() {
console.log('in comp mount')
alert("in comp mount")
const { path } = this.props;
import(`${path}`)
.then(module => this.setState({ module: module.default }))
}
render() {
console.log('in render')
alert("in render")
const { module: Component } = this.state; // Assigning to new variable names #see: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment
return(
<div>
{Component && <Component />}
</div>
)
}
}
ReactDOM.render(<Dynamic path='./FirstComponent' />, document.getElementById('root'));
Note that every time the alerts are getting hit and displayed in browser but the console messages are getting logged very inconsistently and the message in componentDidMount() function isn't getting printed at all. i tried the same thing with a bare bones standard create-react-app project and it displays the messages correctly so i am guessing it has something to do with dynamic import
Do **not call setState in componentDidMount(), this a lint in eslint, refer to this link for details.
"react/no-did-mount-set-state"
By default this rule forbids any call to this.setState in componentDidMount outside of functions.
The following patterns are considered warnings:
var Hello = createReactClass({
componentDidMount: function() {
this.setState({
name: this.props.name.toUpperCase()
});
},
render: function() {
return <div>Hello {this.state.name}</div>;
}
});
Therefore follow the below code snippet pattern instead
var Hello = createReactClass({
componentDidMount: function() {
this.onMount(function callback(newName) {
this.setState({
name: newName
});
});
},
render: function() {
return <div>Hello {this.state.name}</div>;
}
});
I created an app to learn ReactJS. Unfortunately, when I was trying to use context I got 1 error on rendering, but my app compiles well.
This is my code:
import React, {Component} from 'react';
const LoginContext = React.createContext(null);
const user = {
isLoggedIn: true,
username: 'test',
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
isLoggedIn: false,
user: user,
};
}
render() {
return (
<LoginContext.Provider user={this.state.user}>
<Welcome/>
</LoginContext.Provider>
);
}
}
class Welcome extends Component {
render() {
return (
<div>
<WelcomeText/>
</div>
);
}
}
class WelcomeText extends Component {
render() {
return (
<LoginContext.Consumer>
<div>
{(user) => (<p>{user.username}</p>)}
</div>
</LoginContext.Consumer>
);
}
}
export default App;
This is the error:
updateContextConsumer
http://localhost:3000/static/js/bundle.js:20927:23
20924 | {
20925 | ReactCurrentOwner.current = workInProgress;
20926 | ReactDebugCurrentFiber.setCurrentPhase('render');
> 20927 | newChildren = render(newValue);
| ^ 20928 | ReactDebugCurrentFiber.setCurrentPhase(null);
20929 | } // React DevTools reads this flag.
20930 |
Can you help me solve this?
ContextProvider needs a prop called value and not user
<LoginContext.Provider value={this.state.user}>
<Welcome/>
</LoginContext.Provider>
Also the Context consumer has the children as a function and not the div
class WelcomeText extends Component {
render() {
return (
<LoginContext.Consumer>
{(user) => (<div><p>{user.username}</p></div>)}
</LoginContext.Consumer>
);
}
}
I am currently working on React-16.8.6 and having an interesting bug.
I'm not sure if it's the known issue or not but I am having the same error whenever I have a space between two characters '}' and '<' as you can see it line-30.
After (1) removing the space or (2) completely making a new line with , it was resolved.
Even though I love React a lot, it's not perfect and we can make it better together.
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
I am following this tutorial: http://reactkungfu.com/2015/07/approaches-to-testing-react-components-an-overview/
Trying to learn how "shallow rendering" works.
I have a higher order component:
import React from 'react';
function withMUI(ComposedComponent) {
return class withMUI {
render() {
return <ComposedComponent {...this.props}/>;
}
};
}
and a component:
#withMUI
class PlayerProfile extends React.Component {
render() {
const { name, avatar } = this.props;
return (
<div className="player-profile">
<div className='profile-name'>{name}</div>
<div>
<Avatar src={avatar}/>
</div>
</div>
);
}
}
and a test:
describe('PlayerProfile component - testing with shallow rendering', () => {
beforeEach(function() {
let {TestUtils} = React.addons;
this.TestUtils = TestUtils;
this.renderer = TestUtils.createRenderer();
this.renderer.render(<PlayerProfile name='user'
avatar='avatar'/>);
});
it('renders an Avatar', function() {
let result = this.renderer.getRenderOutput();
console.log(result);
expect(result.type).to.equal(PlayerProfile);
});
});
The result variable holds this.renderer.getRenderOutput()
In the tutorial the result.type is tested like:
expect(result.type).toEqual('div');
in my case, if I log the result it is:
LOG: Object{type: function PlayerProfile() {..}, .. }
so I changed my test like:
expect(result.type).toEqual(PlayerProfile)
now it gives me this error:
Assertion Error: expected [Function: PlayerProfile] to equal [Function: withMUI]
So PlayerProfile's type is the higher order function withMUI.
PlayerProfile decorated with withMUI, using shallow rendering, only the PlayerProfile component is rendered and not it's children. So shallow rendering wouldn't work with decorated components I assume.
My question is:
Why in the tutorial result.type is expected to be a div, but in my case isn't.
How can I test a React component decorated with higher order component using shallow rendering?
You can't. First let's slightly desugar the decorator:
let PlayerProfile = withMUI(
class PlayerProfile extends React.Component {
// ...
}
);
withMUI returns a different class, so the PlayerProfile class only exists in withMUI's closure.
This is here's a simplified version:
var withMUI = function(arg){ return null };
var PlayerProfile = withMUI({functionIWantToTest: ...});
You pass the value to the function, it doesn't give it back, you don't have the value.
The solution? Hold a reference to it.
// no decorator here
class PlayerProfile extends React.Component {
// ...
}
Then we can export both the wrapped and unwrapped versions of the component:
// this must be after the class is declared, unfortunately
export default withMUI(PlayerProfile);
export let undecorated = PlayerProfile;
The normal code using this component doesn't change, but your tests will use this:
import {undecorated as PlayerProfile} from '../src/PlayerProfile';
The alternative is to mock the withMUI function to be (x) => x (the identity function). This may cause weird side effects and needs to be done from the testing side, so your tests and source could fall out of sync as decorators are added.
Not using decorators seems like the safe option here.
Use Enzyme to test higher order / decorators with Shallow
with a method called dive()
Follow this link, to see how dive works
https://github.com/airbnb/enzyme/blob/master/docs/api/ShallowWrapper/dive.md
So you can shallow the component with higher order and then dive inside.
In the above example :
const wrapper=shallow(<PlayerProfile name={name} avatar={}/>)
expect(wrapper.find("PlayerProfile").dive().find(".player-profile").length).toBe(1)
Similarly you can access the properties and test it.
You can use 'babel-plugin-remove-decorators' plugin. This solution will let you write your components normally without exporting decorated and un-decorated components.
Install the plugin first, then create a file with the following content, let us call it 'babelTestingHook.js'
require('babel/register')({
'stage': 2,
'optional': [
'es7.classProperties',
'es7.decorators',
// or Whatever configs you have
.....
],
'plugins': ['babel-plugin-remove-decorators:before']
});
and running your tests like below will ignore the decorators and you will be able to test the components normally
mocha ./tests/**/*.spec.js --require ./babelTestingHook.js --recursive
I think the above example is confusing because the decorator concept is used interchangeably with idea of a "higher order component". I generally use them in combination which will make testing/rewire/mocking easier.
I would use decorator to:
Provide props to a child component, generally to bind/listen to a flux store
Where as I would use a higher order component
to bind context in a more declarative way
The problem with rewiring is I don't think you can rewire anything that is applied outside of the exported function/class, which is the case for a decorator.
If you wanted to use a combo of decorators and higher order components you could do something like the following:
//withMui-decorator.jsx
function withMUI(ComposedComponent) {
return class withMUI extends Component {
constructor(props) {
super(props);
this.state = {
store1: ///bind here based on some getter
};
}
render() {
return <ComposedComponent {...this.props} {...this.state} {...this.context} />;
}
};
}
//higher-order.jsx
export default function(ChildComp) {
#withMui //provide store bindings
return class HOC extends Component {
static childContextTypes = {
getAvatar: PropTypes.func
};
getChildContext() {
let {store1} = this.props;
return {
getAvatar: (id) => ({ avatar: store1[id] });
};
}
}
}
//child.js
export default Child extends Component {
static contextTypes = {
getAvatar: PropTypes.func.isRequired
};
handleClick(id, e) {
let {getAvatar} = this.context;
getAvatar(`user_${id}`);
}
render() {
let buttons = [1,2,3].map((id) => {
return <button type="text" onClick={this.handleClick.bind(this, id)}>Click Me</button>
});
return <div>{buttons}</div>;
}
}
//index.jsx
import HOC from './higher-order';
import Child from './child';
let MyComponent = HOC(Child);
React.render(<MyComponent {...anyProps} />, document.body);
Then when you want to test you can easily "rewire" your stores supplied from the decorator because the decorator is inside of the exported higher order component;
//spec.js
import HOC from 'higher-order-component';
import Child from 'child';
describe('rewire the state', () => {
let mockedMuiDecorator = function withMUI(ComposedComponent) {
return class withMUI extends Component {
constructor(props) {
super(props);
this.state = {
store1: ///mock that state here to be passed as props
};
}
render() {
//....
}
}
}
HOC.__Rewire__('withMui', mockedMuiDecorator);
let MyComponent = HOC(Child);
let child = TestUtils.renderIntoDocument(
<MyComponent {...mockedProps} />
);
let childElem = React.findDOMNode(child);
let buttons = childElem.querySelectorAll('button');
it('Should render 3 buttons', () => {
expect(buttons.length).to.equal(3);
});
});
I'm pretty sure this doesn't really answer your original question but I think you are having problems reconciling when to use decorators vs.higher order components.
some good resources are here:
http://jaysoo.ca/2015/06/09/react-contexts-and-dependency-injection/
https://medium.com/#dan_abramov/mixins-are-dead-long-live-higher-order-components-94a0d2f9e750
https://github.com/badsyntax/react-seed/blob/master/app/components/Menu/tests/Menu-test.jsx
https://github.com/Yomguithereal/baobab-react/blob/master/test/suites/higher-order.jsx
In my case decorators are very useful and I dont want to get rid of them (or return wrapped and unwrapped versions) im my application.
The best way to do this in my opinion is to use the babel-plugin-remove-decorators (which can be used to remove them in tests) has Qusai says, but I wrote the pre-processor differently like below:
'use strict';
var babel = require('babel-core');
module.exports = {
process: function(src, filename) {
// Ignore files other than .js, .es, .jsx or .es6
if (!babel.canCompile(filename)) {
return '';
}
if (filename.indexOf('node_modules') === -1) {
return babel.transform(src, {
filename: filename,
plugins: ['babel-plugin-remove-decorators:before']
}).code;
}
return src;
}
};
Take notice of the babel.transform call that im passing the babel-plugin-remove-decorators:before element as an array value, see: https://babeljs.io/docs/usage/options/
To hook this up with Jest (which is what I used), you can do it with settings like below in your package.json:
"jest": {
"rootDir": "./src",
"scriptPreprocessor": "../preprocessor.js",
"unmockedModulePathPatterns": [
"fbjs",
"react"
]
},
Where preprocessor.js is the name of the preprocessor.