Unexpected token, expected "," when trying to generate dynamic components - javascript

When I try to generate only a specific component based on a criteria in react, i get unexpected token error. I'm using an arrow function inside a map function to check if any of the elements satisfy the criteria (the element should be 1 to be shown).
I have tried putting the <{section} /> and null into parentheses but i don't think that's the problem. Here is my code:
import One from './components/One';
import Two from './components/Two';
import Three from './components/Three';
class App extends React.Component {
constructor(props) {
this.state = {
section_to_show: {
'One': 1,
'Two': 0,
'Three': 0,
},
};
}
render() {
...
const section_renders = Object.keys(this.state.section_to_show).map(
section => (
this.state.section_to_show[section] ? <{section} /> : null
)
);
return (
...
{section_renders}
...
);
}
};
What am i doing wrong? I just can't seem to figure it out.

I suspect you want to do:
import React from "react";
import ReactDOM from "react-dom";
const One = () => <div>1</div>;
const Two = () => <div>2</div>;
const Three = () => <div>3</div>;
class App extends React.Component {
state = {
section_to_show: {
One: <One />,
Two: <Two />,
Three: <Three />
}
};
render() {
const section_renders = ["One", "Two", "Three"].map(section =>
this.state.section_to_show[section]
? this.state.section_to_show[section]
: null
);
return <div>{section_renders}</div>;
}
}
ReactDOM.render(<App />, document.getElementById("root"));
Your code: <{section} /> is invalid because you are trying to insert content within a fragment instead of wrapping a fragment tag around it <>{content} </>
Example codesandbox: https://codesandbox.io/embed/react-component-7mvbk

Okay, i see what you are trying to achieve,
First of all map returns an array and component name start from a capital letter that's how jsx distinguish between html elements and react Components
so heres the solution
const section_renders = ['One', 'Two'].map(
section => {
return (
this.state.section_to_show[section] ? this.state.section_to_show[section] : null
)
}
);
return (
<div>{section_renders.map(section => {
if(section) {
const Comp = section;
return <Comp />
} else {
return null
}
})}</div>
);
and your state object should look like this
state = {
section_to_show: {
One,
}
}
If you are still having some problem i have created a Sandbox for you convenience
one other thing if your array have small letters like you have ['one'] you can easily map it to an object but i hope that's not too difficult
Hope it helps

Related

Simple for loop in React expects identifier

I'm new to react and am trying to implement a simple for loop, as demonstrated in this other stackoverflow post. However, I cannot seem to make it work. I just want to run the component 5 (or any number of) times rather than map over an array or similar.
DEMO: https://stackblitz.com/edit/react-ekmvak
Take this example here:
index.js:
import React, { Component } from 'react';
import { render } from 'react-dom';
import './style.css';
import Test from './test';
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
render() {
return (
<div>
for (var i=0; i < 5; i++) {
<Test />
}
</div>
);
}
}
render(<App />, document.getElementById('root'));
test.js
import React from "react";
export default function Test() {
return (
<p>test</p>
);
}
Can anyone tell me where I'm going wrong? I've tried to copy the other stackoverflow post and tried test() also. I still get this error:
Error in index.js (18:27) Identifier expected.
Thanks for any help here.
You're trying to use plain Javascript in JSX. You have the right idea but your syntax is wrong. Instead, move your Javascript code (for loop) out to your render() method (above the return())
render() {
let items = []
for (let i = 0; i < 5; i++) {
items.push(<Test key={i} />)
}
return (
<div>
{items}
</div>
);
}
Few things to note here:
Components that are being iterated over, need a unique key property. In this case, we can use the current value of i
Elements can be rendered in JSX by wrapping them in curly braces, shown above. { items }
JSX will accept you any valid JavaScript expressions, Declarative vs Imperative Programming maybe this source can help you. You can create a declarative solution like those shown by the other colleagues (and the best solution), and also you can wrap your imperative solution into a function or method.
const Test = () => {
return (
<p>test</p>
);
}
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
createElements = () => {
const elments = [];
for (var i=0; i < 5; i++) {
elments.push(<Test />)
}
return elements;
}
render() {
return (
<div>
{this.createElements()}
</div>
);
}
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<div id="react"></div>
<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>
You need a returned value inside the JSX to be able to display anything, here's how you can do that:
const Test = () => {
return (
<p>test</p>
);
}
class App extends React.Component {
constructor() {
super();
this.state = {
name: 'React'
};
}
render() {
return (
<div> { Array.from(Array(5)).map(el => <Test />) } </div>
);
}
}
// Render it
ReactDOM.render(
<App />,
document.getElementById("react")
);
<div id="react"></div>
<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>
You can't use a for loop like this in your return. I would recommend you using a map for this and looping over an array. You can do this by simply creating an array and directly mapping over it :
[...Array(totalSteps)].map(el => el {
return (
<Test />
)
})
You will have to surround this whole block in {}. This will create an array of totalSteps items and return totalSteps <Test />. So if totalSteps is 5, you'll be rendering that component 5 times. This is how your final component should look like :
render() {
return (
<div>
{[...Array(totalSteps)].map(el => el {
return (
<Test />
)
})}
</div>
);
}
For Dynamic Implementation, you can just pass an object to the parameter and display its different values in different components.
We will use map to iterate through the array of objects. Following is the example code in this regard:
return (
<>
{userData.map((data,index)=>{
return <div key={index}>
<h2>{data.first_name} {data.last_name}</h2>
<p>{data.email}</p>
</div>
})}
</>
In my scenerio, the following code helped me to generically generate multiple cards on the same page using a loop (map):
{data.map((data1, id)=> {
return <div key={id} className='c-course-container-card'>
<Courses
courseid = {data1.courseid}
courselink = {data1.courselink}
img = {data1.imgpath}
coursetitle = {data1.coursetitle}
coursedesc = {data1.coursedesc}
/>
</div>
})}
Hope it helps! :)

How to handle multiple context within React?

New to React - I am trying to use multiple contexts within my App component, I tried following the official guide on multiple contexts.
Here is my current code:
App.js
import React from "react";
import { render } from "react-dom";
import Login from "./Login";
import AuthContext from "./AuthContext";
import LayoutContext from "./LayoutContext";
import LoadingScreen from "./LoadingScreen";
class App extends React.Component {
render() {
const { auth, layout } = this.props;
return (
<LayoutContext.Provider value={layout}>
<LoadingScreen />
<AuthContext.Provider value={auth}>
<AuthContext.Consumer>
{auth => (auth.logged_in ? console.log("logged in") : <Login />)}
</AuthContext.Consumer>
</AuthContext.Provider>
</LayoutContext.Provider>
);
}
}
render(<App />, document.getElementById("root"));
Login.js
import React from "react";
class Login extends React.Component {
render() {
return (
<div></div>
);
}
}
export default Login;
AuthContext.js
import React from "react";
const AuthContext = React.createContext({
logged_in: false
});
export default AuthContext;
LayoutContext.js
import React from "react";
const LayoutContext = React.createContext({
show_loading: false
});
export default LayoutContext;
LoadingScreen.js
import React from "react";
import LayoutContext from "./LayoutContext";
class LoadingScreen extends React.Component {
render() {
return (
<LayoutContext.Consumer>
{layout =>
layout.show_loading ? (
<div id="loading">
<div id="loading-center">
<div className="sk-chasing-dots">
<div className="sk-child sk-dot1"></div>
<div className="sk-child sk-dot2"></div>
</div>
</div>
</div>
) : null
}
</LayoutContext.Consumer>
);
}
}
export default LoadingScreen;
Following the example, I never really understood how this.props (in App.js) could hold my different contexts.
Both auth and layout show up as undefined, this.props is empty, which will in turn cause my app to throw errors such as Cannot read property 'show_loading' of undefined
I immediately liked the example provided in the React documentation, but I can't get this to work.
I've made a small snippet to show you how you could structure your context providers and consumers.
My App component in this case is the root of the app. It has all the providers, along with the value for each one of them. I am not changing this value, but I could if I wanted to.
This then has a single child component, MyOutsideComponent, containing all the chained consumers. There are better ways to do this, I just wanted to show you, one by one, how chaining consumers work. In practice you can neatly reduce this using a few techniques.
This MyOutsideComponent has the actual component, MyComponent, which takes all the context elements and just puts their value on the page. Nothing fancy, the point was to show how the values get passed.
let FirstContext = React.createContext('first');
let SecondContext = React.createContext('second');
let ThirdContext = React.createContext('third');
let FourthContext = React.createContext('fourth');
let MyComponent = (props) => {
return (<span >{Object.values(props).join(" ")}</span>);
};
let App = (props) => {
return (
<FirstContext.Provider value="this is">
<SecondContext.Provider value="how you">
<ThirdContext.Provider value="pass context">
<FourthContext.Provider value="around">
<MyOutsideComponent />
</FourthContext.Provider>
</ThirdContext.Provider>
</SecondContext.Provider>
</FirstContext.Provider>
);
};
let MyOutsideComponent = () => {
return ( < FirstContext.Consumer >
{first =>
(< SecondContext.Consumer >
{second =>
(< ThirdContext.Consumer >
{third =>
(<FourthContext.Consumer >
{fourth =>
(<MyComponent first={first} second={second} third={third} fourth={fourth} />)
}
</FourthContext.Consumer>)
}
</ThirdContext.Consumer>)
}
</SecondContext.Consumer>)
}
</FirstContext.Consumer>);
}
ReactDOM.render(<App />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app"></div>
Now, for the actual explanation. createContext gives you two actual components: a Provider and Consumer. This Provider, as you found out, has the value. The Consumer takes as child a single function taking one argument, which is your context's value.
This is where the docs are a bit unclear, and a bit which I hope I can help a bit. This does not get passed automatically in props unless the Provider is the direct parent of the component. You have to do it yourself. So, in the example above, I chained four consumers and then lined them all up in the props of my component.
You've asked about class-based components, this is how it ends up looking like:
let FirstContext = React.createContext('first');
let SecondContext = React.createContext('second');
let ThirdContext = React.createContext('third');
let FourthContext = React.createContext('fourth');
class MyComponent extends React.Component {
render() {
return ( < span > {Object.values(this.props).join(" ")} < /span>);
}
}
class App extends React.Component {
render() {
return (
<FirstContext.Provider value = "this is" >
<SecondContext.Provider value = "how you" >
<ThirdContext.Provider value = "pass context" >
<FourthContext.Provider value = "around" >
<MyOutsideComponent / >
</FourthContext.Provider>
</ThirdContext.Provider >
</SecondContext.Provider>
</FirstContext.Provider >
);
}
}
class MyOutsideComponent extends React.Component {
render() {
return (
<FirstContext.Consumer >
{ first =>
(< SecondContext.Consumer >
{ second =>
( < ThirdContext.Consumer >
{ third =>
( < FourthContext.Consumer >
{ fourth =>
( < MyComponent first = {first} second={second} third={third} fourth={fourth} />)
}
</FourthContext.Consumer>)
}
</ThirdContext.Consumer>)
}
</SecondContext.Consumer>)
}
</FirstContext.Consumer>
);
}
}
ReactDOM.render( < App / > , document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app" />

Making a section change by clicking a button

i am making a website under react with reactstrap, i have a section that contains charts and a button whose function is to replace said charts with another chart containing more details. however i am struggling to make a concrete code.
i have tried placing the charts in a separate component and have it's content switch through the use of a handleclick function on the button that changes the state of the section (using 'onclick')
i am really not confident in my code's clarity, so i tried reproducing what i did in a simpler matter within fiddle
class hello extends React.Component {
render() {
return (
<h2>hello</h2>
);
}
}
class bye extends React.Component {
render() {
return (
<h2>goodbye</h2>
);
}
}
class Toggle extends React.Component {
constructor(props) {
super(props);
this.state = {isToggleOn: true};
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
render() {
return (
<div>
<div>
{this.state.components[hello]}
</div>
<button onClick={this.handleClick}>
switch
{this.setState({components:[<bye />]})}
</button>
</div>
);
}
}
ReactDOM.render(
<Toggle />,
document.getElementById('root')
);
the div in the "toggle" component is supposed to switch between the components "hello" and "bye"
in effect the current section that is supposed to be displayed ("hello") will be replaced by the other section ("bye") uppon clicking the button under them.
thanks in advance.
If you simply want to toggle between the two components with the button click, you can use conditional rendering.
Change your render method to this:
render(){
return (
<div>
{this.state.isToggleOn?<Hello />:<Bye />}
<button onClick={this.handleClick}>Switch</button>
</div>
}
Also keep your Component's name first character capitalized or react might complain. And using Class based Components is outdated. Hooks are the hot thing right now. So try to use more Functional Components.
Note: My answer assumes you are using babel presets for transpiling jsx and es6 syntax. If not, check out #Colin's answer. It also uses hooks.
why not import all partial views and conditionally render them based on the condition
{condition & <View1/>
There's a few mistakes in your code. Here's an example which does what you want using conditional rendering:
import React, { useState } from "react";
import ReactDOM from "react-dom";
const Hello = () => {
return <h2>hello</h2>;
};
const Bye = () => {
return <h2>bye</h2>;
};
const App = () => {
const [toggled, setToggled] = useState(true);
const handleClick = () => {
setToggled(!toggled);
};
const render = () => {
if (toggled) {
return <Hello />;
}
return <Bye />;
};
return (
<div>
<button onClick={handleClick}>toggle</button>
{render()}
</div>
);
};
ReactDOM.render(<App />, document.getElementById("root"));
There are many ways to do it:
Using conditional operator:
{ this.state.isToggleOn?<Hello/>:<Bye/> }
Using if condition:
render() {
let chart;
if(this.state.isToggleOn) {
chart = <Hello/>;
} else {
chart = <Bye/>;
}
return ( <div> { chart } </div>);
}
3 You can use switch case also for conditional rendering. Here it is not well suited as condition is true or false.

React HOC class returns nothing

I am facing a problem I can't figure out. For a project we use React to generate a layout from JSON input using the following code (simplified):
function generateComponents(children, params) {
let comps = [];
if (!children || children && children.length === 0) {
return [];
}
forEach(children, (comp) => {
let compName = comp.component;
let createdComp;
switch (compName) {
case 'test1':
createdComp = TestOne(Object.assign({}, comp, params));
break;
case 'test2':
createdComp = TestTwo(Object.assign({}, comp, params));
break;
}
comps.push(createdComp)
}
}
return comps.length === 1 ? comps[0] : comps;
}
This works well and the layout is generated correctly. We wanted to take this a step further and wrap the createdComp in a Higher Order Component. We implemented that in the following way:
function generateComponents(children, params) {
// see above for implementation
let component;
if (condition)
component = testingHOC(createdComp);
else
component = createdComp
comps.push(component);
}
// TestingHOC.js
export function testingHoc(WrappedComponent) {
console.log('wrapped')
return class TestingHoc extends Component {
render() {
console.log('props TestingHOC', this.props);
return ( <WrappedComponent { ...this.props} />);
}
}
};
This broke our component generation. The code returns nothing. The only thing that gets logged is the console.log('wrapped'), the render function of the class is never called. What are we missing here?
EDIT:
Render method of the render class:
render() {
const children = this.state.children;
const {params} = this.props;
const arrChildren = isArray(children) ? children : [children];
let comps = generateComponents(arrChildren, params || {});
if (isArray(comps)) {
return (
<ViewComponent>
{comps}
</ViewComponent>
);
} else {
return comps;
}
}
EDIT 2:
Console.log of {comps} with the testingHoc
Console.log of {comps} without the testingHoc
Edit 3
Added the code for ViewComponent:
import React from 'react';
const ViewComponent = (props) => (
<div {...props}/>
);
export default ViewComponent;
The issue you are facing is because of the inherent difference between a React element and a React component.
When you are not using the HOC, you are creating a React element which can be seen by the first console.log image. This is the output after reconciliation has occurred.
When you use the HOC, your HOC returns a component which shows up as the test(props) function in your second console.log image.
To have the same functionality with your HOC enhanced components, you need to change the code in generateComponents functions to
if (condition){
let Comp = testingHOC(createdComp);
component = <Comp/>;
}
Try
...
return (
<ViewComponent>
{comps.map((Comp) => <Comp />)}
</ViewComponent>
);
...
or
...
return (
<ViewComponent>
{comps.map((comp) => comp())}
</ViewComponent>
);
...

How can I dynamically create a component based on a prop or variable using React.createElement

I have 2 types of components, for example a <Promo /> and an <Announcement />
One of my components maps over a list of items and creates either promos or announcements, how can I pass an ItemElement, rather than have to repeat the mapping based on an if statement.
current implementation
import React, { Component } from 'react'
import Promo from './Promo'
import Announcement from './Announcement'
class Demo extends Component {
render() {
const { ItemElement } = this.props
let items = null
if(ItemElement === 'Promo'){
items = this.props.items.map((p, i) => (
<Promo item={p} />
))
} else if(ItemElement === 'Announcement') {
items = this.props.items.map((a, i) => (
<Announcement item={a} />
))
}
return (
{ items }
)
}
}
desired implementation not working
import React, { Component } from 'react'
import Promo from './Promo'
import Announcement from './Announcement'
class Demo extends Component {
render() {
// this would be 'Promo' or 'Announcement'
const { ItemElement } = this.props
let items = this.props.items.map((p, i) => (
<ItemElement item={p} />
))
return (
{ items }
)
}
}
This works fine if I pass in say a 'div' or 'a' or 'span' tag, but not for my own components?
Your render() method needs to return a single element. Right now you're returning a javascript object with a single property: items. You need to contain those items in a top level element, either another Component, or a DOM element (<div> or <span> or the like).
As for passing a component in as a prop, there's no reason you shouldn't be able to do that:
class Demo extends React.Component {
render() {
// this would be 'Promo' or 'Announcement'
const { ItemElement } = this.props
let items = this.props.items.map((p, i) => (
<ItemElement item={p} />
))
return <ul>{items}</ul>;
}
}
class Promo extends React.Component {
render() {
return <li>Promo - {this.props.item}</li>;
}
}
class Announcement extends React.Component {
render() {
return <li>Announcement - {this.props.item}</li>;
}
}
const items = [
"foo",
"bar"
];
ReactDOM.render(
<Demo
ItemElement={Promo} // <- try changing this to {Announcement}
items={items}
/>,
document.getElementById('app')
);
Here's a jsbin demonstrating: http://jsbin.com/cakumex/edit?html,js,console,output

Categories