Unexpected token '<' even when text/babel is used on script - javascript

I'm trying to render a button in React using perfectly valid JSX (at least, as far as I can tell after looking at it for 20 minutes). But for some reason I keep getting the syntax error:
Unexpected token '<' on line 18
This comes as a surprise to me considering when I load the script I'm both using the text/babel type attribute, and the babel script is loaded in the <head> while this one is loaded in the DOM.
Here's what my HTML looks like:
<head>
<!-- react, react-dom, axios etc. go here !-->
<script crossorigin src="https://unpkg.com/babel-standalone#6.26.0/babel.min.js"></script>
</head>
<body>
<div id="content"></div>
<!-- notice that i'm using babel !-->
<script type="text/babel" src="cdn/static/scripts/module/user.js">
</body>
And here is my JavaScript:
class User extends Component{
constructor(props){
super();
this.user = props.user;
this.state = {
modalOpen: false
};
}
Button = () => {
let user = this.user;
// vvvv this is what the error points to
return <button className={(user.loggedIn ? "user" : "sign-in")}>{(user.loggedIn ? user.public.display : "Sign In")}</button>;
}
render(){
return (
<div className="button">
<this.Button />
</div>
);
}
}
ReactDOM.render(<User user={window.User} />, document.getElementById("content"));
What is even stranger is that it will actually render the button, but because there is an error, it will also cause the rest of my JavaScript to break.
I've never come across this problem before so all help is appreciated, cheers.

You don't have React scripts attached see how to create React app.
Because JSX is syntatic sugar for React.createElement, and custom components must be Uppercased, you should render Button like so:
class User extends Component{
constructor(props){
super();
this.user = props.user;
this.state = { modalOpen: false };
}
Button = () => {
let user = this.user;
return (
<button className={user.loggedIn ? "user" : "sign-in"}>
{user.loggedIn ? user.public.display : "Sign In"}
</button>
);
}
render(){
const Button = this.Button;
return (
<div className="button">
<Button />
</div>
);
}
}
Or invoke React.createElement directcly:
<div className="button">
{React.createElement(this.Button, null)}
</div>
See JSX in Depth.

Related

Uncaught TypeError: e.preventDefault is not a function

I am new to ReactJS and trying to create a simple todo list app from a tutorial.
This is the code in App.js:
addItem = e => {
e.preventDefault();
const newItem = this.state.currentItem;
if (newItem.text !== "") {
const items = [...this.state.items, newItem];
this.setState({
items: items,
currentItem: { text: "", key: "" }
});
}
};
It throws:
Uncaught TypeError: e.preventDefault is not a function
at App._this.addItem (App.js:89)
If I try to change it to e.preventDefault and bind it in constructor, the error goes away but the form still reloads on submit (i.e preventDefault does not work). Can someone please help me with this?
Editing to add how addItem is called:
This is my TodoList.js:
import React, {Component} from 'react'
class TodoList extends Component{
componentDidUpdate(){
if(this.props.inputElement.current){
this.props.inputElement.current.focus();
}
}
render(){
return(
<div className="todoListMain">
<div className="header">
<form onSubmit={this.props.addItem}>
<input
placeholder="Task"
ref={this.props.addItem}
value={this.props.currentItem.text}
onChange={this.props.handleInput}
/>
<button type="submit">Add Task</button>
</form>
</div>
</div>
)
}
}
export default TodoList;
Remove the ref attribute in the input tag, it'll works fine https://stackblitz.com/edit/react-womnwm
ref in the input field passes an object with some data about the tag with properties like tagName, attributes, innerHTML etc., and it wouldn't have a preventDefault method

Manipulate HTML5 dialog in React

I'm trying to create a simple configuration dialog with close icon on top-right, but can't think of how to handle this in React. In other frameworks, I can simply use selector, and then use .showModal()/close() to open/close a dialog. However, I think we're not allowed, or not recommended to directly manipulate DOM in React, so I wonder what's the proper way in this case.
My project outline
App.js
class App extends Component {
...
...
return(
<div>
<ConfigPage />
<ConfigButton />
<MainContents />
</div>
)
}
I want to open a dialog, which is <ConfigPage />, by pressing the <ConfigButton /> I set, and close it by pressing the icon on the dialog.
config-page.js
class ConfigPage extends Component {
...
...
return(
<dialog>
<header>
<div>
<i onClick={someCallback}></i>
</div>
</header>
<section></section>
</dialog>
)
}
the HTML5 dialog also has an open attribute, correct? Instead of calling show/hide you could manipulate this attribute -
class ConfigPage extends Component {
...
...
return(
<dialog open={this.state.showDialog ? 'open' : false}>
<header>
<div>
<i onClick={someCallback}></i>
</div>
</header>
<section></section>
</dialog>
)
}
And when you want to show/hide call this.setState({showDialog: true}) (or false)
Here's a js fiddle with a proof-of-concept: https://jsfiddle.net/n5u2wwjg/193969/
Welcome to SO. You can hide a react component by return null from the render function. You can define a flag in the state that determines weather or not your component is visible. Here is a simple example.
class Modal extends Component {
constructor(props) {
super(props);
this.state = {
isOpen: true;
};
this.onCloseClick = this.onCloseClick.bind(this);
}
onCloseClick(e) {
e.preventDefault();
this.setState({
isOpen: false,
});
}
render(){
if (!this.state.isOpen) return null;
return (
<div>
<button onClick={this.onCloseClick}>
Close
</button>
<h1>What up, this a modal</h1>
<div>
);
}
}

How do you change what jsx blocks are being rendered from inside a same class?

I'm still learning the basics of React, and I wanted to do something that caught my attention. It's about rendering two things. Is it possible or even plausible to just change what is rendered with a function, and then calling the functions separately with a button or timer?
This is a sample code, to show how it would render two completely different things. It is supposed to render a button that says "State: On" or "State: Off". And when you click the button the state changes. But also, the whole render method is switched... or at least that's what's supposed to be happening.
class Flicker{
constructor(props){
super(props);
this.state = {mode: "on"};
}
flipOn(){
this.setState({mode: "on"})
}
flipOff(){
this.setState({mode: "off"})
}
if (this.state.mode == "on"){
render() {
return(
<button onClick={this.flipOn}>State: On</button>
);
}
} else if (this.state.mode == "off"){
render() {
return(
<button onClick={this.flipOff}>State: Off</button>
);
}
}
}
export default Flicker;
If this isn't the correct way to do this type of changes in what jsx gets rendered on the app, how should it be done?
You have the right idea - but in a class there is only one render method. Your logic does belong inside the render. This should do what you're looking for:
class Flicker extends React.Component {
constructor(props){
super(props);
this.state = {mode: "On"};
}
flipOn(){
this.setState({mode: "On"})
}
flipOff(){
this.setState({mode: "Off"})
}
render () {
return (
<button onClick={(this.state.mode === 'Off') ? this.flipOn : this.flipOff}>State: {this.state.mode}</button>
)
}
}
export default Flicker;
Put the conditional logic inside the render() method.
Something like this...
class Example extends React.Component {
// the rest of your code
render() {
const { mode } = this.state;
return(
<div>
{mode ==="on" && <button onClick={this.flipOn}>State: On</button> }
{mode === "off" && <button onClick={this.flipOff}>State: Off</button>}
</div>
)
}
}
Your component changes the state. The state is boolean - either on (true) or not (false). Since you're state is switched between two value, you can use a single method (toggle). Since we check the previous state, it it's better to setState via updater.
You need to bind the method to this, by using bind in the constructor (method 4) or an arrow function via an instance method (method 5).
Now in the (single) render, you only need to change the text according to the state of on:
class Flicker extends React.Component{
state = { on: true };
toggle = () =>
this.setState(({ on }) => ({ on: !on }));
render() {
const text = this.state.on ? 'On' : 'Off';
return (
<button onClick={this.toggle}>State: {text}</button>
);
}
}
ReactDOM.render(
<Flicker />,
demo
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>
Real world example
The button should probably get on and toggle via props. Now they are available outside, and the Flicker's only concern is calling toggle when it's clicked, and changing the text according to on:
const Toggler = ({ on, toggle }) => (
<button onClick={toggle}>
State: {on ? 'On' : 'Off'}
</button>
);
class Flicker extends React.Component {
state = { on: true };
toggle = () =>
this.setState(({ on }) => ({ on: !on }));
render() {
const { on } = this.state;
return (
<div>
<Toggler toggle={this.toggle} on={on} />
{on &&
'I\'m displayed only when I\'m On'
}
</div>
);
}
}
ReactDOM.render(
<Flicker />,
demo
);
<script crossorigin src="https://unpkg.com/react#16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#16/umd/react-dom.development.js"></script>
<div id="demo"></div>

Show and hide a component based on a variable

I have created a loading icon component, which simply displays a spinner while loading something. I load it into my Sign In component, and wish to display the icon once the user clicks the Login button (And the API call is busy).
So:
import Loading from '../common/loading';
I then set an isLoading variable, defaulted to false:
this.isLoading = false;
Then, within my render method, I wish to determin if I need to show the spinner or not.
render() {
var LoadingSpinner = this.state.canLogin ? Loading : '<div></div>';
This fails.
And then my button is where I show the spinner. I'm hoping to hide the 'Sign In' text, and replace it with the spinner, but ... first thing is to handle the spinner, based on the isLoading variable.
<button
className="btn btn-lg btn-primary btn-block"
type="button"
onClick={this.handleSignin}
disabled={!this.state.canLogin}>
<span>Sign in</span> <LoadingSpinner />
</button>
</div>
Can/should this be done this way, OR... should I maybe pass a prop to my Loading component, called 'Visible' or something, and I set that?
put isLoading to constructor with default false
and then inside the render method, just add a condition
{ this.state.canLogin ? <LoadingSpinner /> : null }
Here is what you could do, using a state variable.
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
loading: false
}
}
onClick = () => {
this.setState({
loading: true
})
}
render() {
return (
<div>
{this.state.loading && <div>Loading</div>}
<button onClick={this.onClick}>Click to Load</button>
</div>
);
}
}
ReactDOM.render( < App / > , document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='root'>
</div>

Race condition with load event in Javascript

So this is more or less the code
Sorry for the syntax, I typed it from my phone
export default class Main extends React.Component {
componentDidMount() {
axios.get('/user?ID=12345')
.then(function (response) {
if (response){
document.addEventListener('load', () => {/* remove spinner using jquery */});
} else { /* redirect to somewhere else */}
})
}
render() {
return (
<SomeComponent />
);
}
}
I used addEventListener with React because I couldn't find any other way to bind the removal of the loading spinner to the load event.
The issue is that there is a race here, for slow network stations or fast CPUs ones, the load event may be launched long before the request is resolved, which results in the loading spinner to remain.
Is there maybe a way to check if the load event was already lanched?
If I can do that, I'll be able to check it after adding the event listener and in case it was already launched, I'll remove the spinner manually.
I would't use jquery for this task (or at all in react) as you can do it in a more "reactish" way.
You can store the data in your state and conditionally render the component in your render method when the state has changed.
You can't avoid the first render by the way.
Small example:
const Loader = () => <div>Loading...</div>
const MyComponent = ({message}) => <div>{message}</div>
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
message: ''
};
}
componentDidMount(){
// mimic async operation
setTimeout(()=>{
this.setState({message: 'Hi there!'})
}, 1500);
}
render() {
const {message} = this.state;
return (
<div>
{message ? <MyComponent message={message} /> : <Loader />}
</div>
);
}
}
ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="root"></div>
Edit
As a followup to your comment:
But then you re-render the entire component just to change the display
style of the preloader element, right?
Not entirely true i think you should read more about Reconciliation and The Diffing Algorithm and look at this example:
React DOM compares the element and its children to the previous one,
and only applies the DOM updates necessary to bring the DOM to the
desired state.

Categories

Resources