Parsing error: Unexpected token when adding a conditional in react - javascript

Hi I'm a beginner in react, I was getting an error for scenarios when no props get passed to my component, it said something like "you can't do a map of undefined" so I added this if statement. But now I get a parsing error on line 4. Not sure' what's wrong with my code:
import React from 'react';
export const Record = props => (
if (props) {
<div className='card-container'>
{props.rows.drivingInstructions.map((val, ind) => {
return (
<tr>
<td>{ind + 1}</td>
<td>Smith</td>
<td>50</td>
</tr>
)
})}
</div>
}
{ console.log(props.rows.drivingInstructions) }
);

if can't exist in an expression context. For similar reasons, you can't do:
const someVar = if (true) 10 else null;
The syntax is not permitted. To do something like this, you need the conditional operator:
export const Record = props => (
!props.rows || !props.rows.drivingInstructions ? null : (
<div className='card-container'>
{props.rows.drivingInstructions.map((val, ind) => {
...
Or, if you're OK with the .card-container existing even if it doesn't have any rows, use optional chaining instead:
export const Record = props => (
<div className='card-container'>
{props.rows.drivingInstructions?.map((val, ind) => {
...

I would rewrite those code as below.
import React from 'react';
export const Record = (props) => {
if (!!props?.rows?.drivingInstructions?.length) {
return (<div className='card-container'>
{props.rows.drivingInstructions.map((val, ind) =>(
<tr>
<td>{ind + 1}</td>
<td>Smith</td>
<td>50</td>
</tr>
)
)}
</div>);
}
return <div>No Driving Instructions</div>;
}
};
This !!props?.rows?.drivingInstructions?.length is an optional chaining and it checks that those properties are not nullish. Lastly, it ensures the length is also more than 0 by converting to boolean using !!.

import React from 'react';
export const Record = props => (
props?.rows?.drivingInstructions?.length ?
(<div className='card-container'>
{props.rows.drivingInstructions.map((val, ind) =>
<tr>
<td>{ind + 1}</td>
<td>Smith</td>
<td>50</td>
</tr>
)}
</div>) : null;
);

Related

How to control JSX component using filter() in React?

I'm currently using child components which returns JSX.
//PARENT COMPONENT
import ApprovalTableElement from './E_Approval_Element.js';
//JSX of E_Approval_Element.js
const [approvalElement, setApprovalElement] = useState([ApprovalTableElement]);
//Add one more approval column
const addApprovalSpace = () => {
setApprovalElement([...approvalElement, ApprovalTableElement]);
};
return (
<div className={styles['EApprovalWriteDiv']}>
<div className={styles['authDiv']}>
{approvalElement.map((element, index) => (
<button>DELETE ONE APPROVAL SECTION</button>
<ApprovalTableElement index={index} />
))}
</div>
</div>
);
};
export default E_Approval_Write;
//CHILD COMPONENT
function ApprovalTableElement() {
return (
<>
<table className={styles['approvalTable']}>
<tbody className={styles['approval']}>
<tr className={styles['name']}>
<th>
<select style={{ marginLeft: 10 }}>
<option>선택</option>
<option>결재자</option>
<option>합의자</option>
</select>
</th>
</tr>
<tr className={styles['signature']}>
<td>
<div>SIGN</div>
</td>
</tr>
<tr className={styles['name']} onClick={memberModalTrigger}>
<td>
<Typography variant='button' display='block'>
</Typography>
</td>
</tr>
</tbody>
</table>
</>
);
}
export default ApprovalTableElement;
with this code, what I'm trying to do is using
{approvalElement.map((element, index) => (
<button>DELETE ONE APPROVAL SECTION</button>
<ApprovalTableElement index={index} />
))}
this button, deleting selected ApprovalTableElement.
right now, I have this UI. When I click + button, I keeps adding component. But when I click remove button, the table attached to the button should disappear. BUT not the other ones.
All I can know is the index of the Component, so I am really confusing on how to delete the targeted component using filter().
OR, should I add button tag inside the CHILD COMPONENT not the PARENT COMPONENT?
However, If I can make what I'm trying to do with this code, please tell me how to set up the code properly to make things possible. Thank you!
Just pick those which id is different from the one you are deleting
const removeApprovalSpace = (id) => {
setApprovalElement(items => items.filter(item => item.id !== id));
};
//usage
<button onClick={() => removeApprovalSpace(id)}>Remove</button>
If you don't have id's you can use index
const removeApprovalSpace = (index) => {
setApprovalElement(items => items.filter((item, i) => i !== index));
};
//usage
<button onClick={() => removeApprovalSpace(index)}>Remove</button>

React Matrix of Radio buttons

I tried to solve this js react problem and get stuck on questions 2-4.
Question 2: I don't know how to check the local state for each row in order to check for the duplicate rank select
Question 3: Should I need props passed to the component to check for unique?
Question 4: How do I check all rows have a select ranked and unique?
Here are the questions:
Adding a class of "done" to a row will highlight it green. Provide this
visual feedback on rows which have a selected rank.
Adding a class of "error" to a row will highlight it red. Provide this
visual feedback on rows which have duplicate ranks selected.
There is a place to display an error message near the submit button. Show
this error message: Ranks must be unique whenever the user has selected the
same rank on multiple rows.
The submit button is disabled by default. Enable it when all rows have a
rank selected and all selected ranks are unique.
The orginal App.js
import React, { Component } from 'react';
import './App.css';
import MainPage from './components/MainPage';
class App extends Component {
render() {
return (
<MainPage />
);
}
}
export default App;
MainPage.js
import React from 'react';
import _ from 'lodash';
import FormRow from './FormRow.jsx';
import Animal from './Animal.js';
class MainPage extends React.Component {
constructor(props) {
super(props);
this.state = {
animals: ['panda','cat','capybara','iguana','muskrat'].map((name) => {
return new Animal(name);
}),
error: ''
};
}
render() {
const rows = this.state.animals.map((animal) => {
return (
<FormRow
animalName={animal.name}
key={animal.name}
/>
);
});
const headers = _.range(1, 6).map((i) => <th key={`header-${i}`}>{i}</th>);
return (
<div>
<table>
<thead>
<tr>
<th></th>
{headers}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div>{this.state.error}</div>
<input type="submit" />
</div>
);
}
}
export default MainPage;
FormRow.jsx
import React from 'react';
import _ from 'lodash';
class FormRow extends React.Component {
render() {
const cells = _.range(1, 6).map((i) => {
return (
<td key={`${this.props.animalName}-${i}`}>
<input
type="radio"
name={this.props.animalName}
value={i}
/>
</td>
);
});
return (
<tr>
<th>{this.props.animalName}</th>
{cells}
</tr>
)
}
}
export default FormRow;
Animal.js
class Animal {
constructor(name, rank) {
this.name = name;
this.rank = rank;
}
}
export default Animal;
My code is at GitHub (git#github.com:HuydDo/js_react_problem-.git). Thanks for your suggestion!
FormRow.jsx
import React from 'react';
import _ from 'lodash';
class FormRow extends React.Component {
constructor(){
super();
this.state = {
rowColor : false,
name: "",
rank: 0
// panda: 0,
// cat: 0,
// capybara: 0,
// iguana: 0,
// muskrat: 0
}
}
handleChange = (e) => {
if (this.state.rank === e.target.value){
console.log("can't select same rank.")
}
console.log(e.target.name)
console.log(e.target.value)
this.setState({
// [e.target.name]: e.target.value,
name: e.target.name,
rank: e.target.value,
rowColor: true
}, console.log(this.state))
}
handleChange2 = (e) => {
let newName = e.target.name
let newRank = e.target.value
let cRank = this.state.rank
let cName = this.state.name
console.log(this.state)
console.log(`${newRank} ${newName}`)
if(cName !== newName) {
if(cRank !== newRank) {
this.setState({
name : newName,
rank: newRank,
rowColor: true
},()=> console.log(this.state))
}
else {
console.log("can't select same rank")
}
}
// this.setState(previousState => {
// let cRank = previousState.rank
// let cName = previousState.name
// console.log(previousState)
// return {
// rank: newRank,
// name: newName,
// rowColor: true
// }
// },console.log(this.state.rank))
}
render() {
const cells = _.range(1, 6).map((i) => {
return (
<td key={`${this.props.animalName}-${i}`} onChange={this.handleChange2}>
<input
type="radio"
name={this.props.animalName}
value={i}
/>
</td>
);
});
return (
<tr className = {(this.state.rowColor) ? 'done':null} >
{/* <tr> */}
<th>{this.props.animalName}</th>
{cells}
</tr>
)
}
}
export default FormRow;
MainPage.jsx
import React from 'react';
import _ from 'lodash';
import FormRow from './FormRow.jsx';
import Animal from './Animal.js';
class MainPage extends React.Component {
constructor(props) {
super(props);
this.state = {
animals: ['panda','cat','capybara','iguana','muskrat'].map((name) => {
return new Animal(name);
}),
error: ''
};
}
getValue = ({name,rank}) =>{
console.log(`Name: ${name} rank: ${rank}`)
}
// handleSubmit = event => {
// event.preventDefault()
// this.props.getValue(this.state)
// }
checkForUnique = () => {
// Show this error message: `Ranks must be unique` whenever the user has selected the
// same rank on multiple rows.
this.setState({
error : "Ranks must be unique"
})
}
isDisabled = () =>{
// The submit button is disabled by default. Enable it when all rows have a
// rank selected and all selected ranks are unique.
return true
}
render() {
const rows = this.state.animals.map((animal) => {
return (
<FormRow
animalName={animal.name}
key={animal.name}
rank={animal.rank}
handleChange={this.handleChange}
getValue={this.getValue}
/>
);
});
const headers = _.range(1, 6).map((i) => <th key={`header-${i}`}>{i}</th>);
return (
<div>
{/* <form onSubmit={this.onSubmit}> */}
<table>
<thead>
<tr>
<th></th>
{headers}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div>{this.state.error}</div>
<input type="submit" value="Submit" disabled={this.isDisabled()} /> {/* <button type="submit">Submit</button> */}
{/* </form> */}
</div>
);
}
}
export default MainPage;
enter image description here
I tried to add handleChange and handleAnimalSelect methods, but I get an error. The new name and rank are not added to the arrays.
MainPage.jsx
import React from 'react';
import _ from 'lodash';
import FormRow from './FormRow.jsx';
import Animal from './Animal.js';
class MainPage extends React.Component {
constructor(props) {
super(props);
this.state = {
animals: ['panda','cat','capybara','iguana','muskrat'].map((name) => {
return new Animal(name);
}),
error: ''
};
}
isDisabled = () =>{
// The submit button is disabled by default. Enable it when all rows have a
// rank selected and all selected ranks are unique.
return true
}
render() {
const rows = this.state.animals.map((animal) => {
return (
<FormRow
animalName={animal.name}
key={animal.name}
rank={animal.rank}
getValue={this.getValue}
handleAnimalSelect={this.handleAnimalSelect}
/>
);
});
const headers = _.range(1, 6).map((i) => <th key={`header-${i}`}>{i}</th>);
return (
<div>
{/* <form onSubmit={this.onSubmit}> */}
<table>
<thead>
<tr>
<th></th>
{headers}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div>{this.state.error}</div>
<input type="submit" value="Submit" disabled={this.isDisabled()} />
{/* <button type="submit">Submit</button> */}
{/* </form> */}
</div>
);
}
}
export default MainPage;
FormRow.jsx
import React from 'react';
import _ from 'lodash';
import FormRow from './FormRow.jsx';
import Animal from './Animal.js';
class MainPage extends React.Component {
constructor(props) {
super(props);
this.state = {
animals: ['panda','cat','capybara','iguana','muskrat'].map((name) => {
return new Animal(name);
}),
error: ''
};
}
isDisabled = () =>{
// The submit button is disabled by default. Enable it when all rows have a
// rank selected and all selected ranks are unique.
return true
}
render() {
const rows = this.state.animals.map((animal) => {
return (
<FormRow
animalName={animal.name}
key={animal.name}
rank={animal.rank}
getValue={this.getValue}
handleAnimalSelect={this.handleAnimalSelect}
/>
);
});
const headers = _.range(1, 6).map((i) => <th key={`header-${i}`}>{i}</th>);
return (
<div>
{/* <form onSubmit={this.onSubmit}> */}
<table>
<thead>
<tr>
<th></th>
{headers}
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
<div>{this.state.error}</div>
<input type="submit" value="Submit" disabled={this.isDisabled()} />
{/* <button type="submit">Submit</button> */}
{/* </form> */}
</div>
);
}
}
export default MainPage;
You're pretty much making a form with a few rows of multiple choice answers.
One way of simplifying everything is to have all the logic in the top component, in your case I think MainPage would be where it would be. Pass down a function as a prop to all the descendants that allows them to update the form data upstream.
In Q2, how do intend to check the state for each row? Perhaps you can use arrays or objects to keep track of the status of each question. The arrays/objects are stored in state, and you just check them to see what the status is.
I'm actually not clear what your app looks like - what does a row look like? (You might want to post a screenshot) And I don't see any way for rank to be selected - I don't even see what the ranks are for, or how they are used in the form. So perhaps your form design needs to be tweaked. You should begin the form design with a clear picture in YOUR mind about how the app will work. Maybe start by drawing the screens on paper and drawing little boxes that will represent the objects/array variables and go through the process of a user using your app. What happens to the various boxes when they click radio buttons and so on. How will you know if the same rank is selected twice - where are the selected ranks stored? What animals are clicked/selected? Where are those stored? Draw it all on paper first.
Array or objects: If you want to keep it simple, you can do the whole project just using arrays. You can have one array that stores all the animals. You can have a different array that stores which animals are selected right NOW (use .includes() to test if an animal is in that array). You can have another array that stores the rows that have a rank selected. When the number of elements in that row === the number of rows (is that the same as the number of animals? If yes, then you can use the length of the animals array for that test)
How do you know if the rows with a rank selected are unique? One way is to DISALLOW selected a rank that has already been selected. Again, use .includes() (e.g. arrSelectedRanks.includes(num)) to check if a rank has already been selected.
SO what do one of these checks look like?
const handleAnimalSelect = (animal) => {
const err = this.state.selectedAnimals.includes(animal);
if (err === true){
this.setState(error: animal);
}
}
return (
<input
type="radio"
name={this.props.animalName}
value={i}
onClick={this.handleAnimalSelect}
/>
{ error !== undefined && (
<div class={style.errorMessage}>{`Animal ${} was chosen twice`}</div>
)}
);
};
Remember: State is what you use for remembering the value of variables in a given component. Every component has its own state. But Props are for data/functions/elements that are passed into the component. You don't update the values of props in the component (prop values are stored in another component. If you need to update them, you use functions to pass data back to the parent component where that variable is in state, and update the value there).
Here is an example that uses .includes() to check for the presence/absence of something:
https://stackoverflow.com/a/64486351/1447509

Why this error arising? users.map is not a function

I am trying to render names from a API with Axios. Even I can see all data in console but unable to render as it shows error:
users.map is not a function
Below I'm sharing my code of the file. I'm quite new that might be the reason I'm unable to figure it out.
import React from 'react';
import './App.css';
import 'bootstrap/dist/css/bootstrap.min.css'
import { Container, Table} from "react-bootstrap";
import axios from 'axios';
class App extends React.Component {
state = {
users: [],
};
componentDidMount () {
axios.get('https://5w05g4ddb1.execute-api.ap-south-1.amazonaws.com/dev/profile/listAll')
.then(response => {
const users = response.data;
this.setState({ users });
console.log(this.state.users);
})
}
render() {
const { users } = this.state;
return (
<div className="App">
<Container fluid>
<Table striped bordered hover size="sm">
<thead>
<tr>
<th><div id="">Image</div></th>
<th>Name</th>
<th>Gender</th>
<th>Age</th>
<th>Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<tr>
{ users.map(user => { return <td key={user.id}>{ user.name }</td> }) }
</tr>
</tbody>
</Table>
</Container>
</div>
)
}
}
export default App;
The response you are getting is not an array, but an object like this:
{"list":[{"id":"MnJJA0dbuw","name":"Anand Sharma","img":"https://incablet-tests.s3.ap-south-1.amazonaws.com/conference-content/photos/sponsors/Anand.jpeg","gender":"m","age":46,"date":"23/11/2019","status":"onboarded"}]}
You can access the array by replacing const users = response.data; with const users = response.data.list;
In your axios get, in the “then” part change the line:
const users = response.data;
To:
const users = response.data.list;
fast hack
{users.list.map(user => { return <td key={user.id}>{ user.name }</td> }) }
There are 2 errors:
the response from api is not a list but an object, u need to go to response.list to use the list from the response
it should be
const users = response.data.list
your
this.setState({users})
,you will need to change it to
this.setState({users:<VARIABLE_NAME_TO_HOLD_USER_DATA>})
even what you write is valid like in ES6, that is somehow a bad idea, it is not a clear what are you doing, just define what u want to copy to ur component state

React/Redux: separating presentational and container components dilemma.

Here's a simplified representation of what I have:
container.js:
import Presentation from './Presentation';
import { connect } from 'react-redux';
export default connect(
({ someState}) => ({ ...someState }),
(dispatch) => {
onClick(e) => ({})
}
)(Presentation);
pesentation.js:
export default ({ items, onClick}) => (
<table>
<tbody>
{items.map((item, index) => (
<tr>
<td onClick={onClick}>{item.a}</td>
<td>
<div onClick={onClick}>{item.b}</div>
</td>
</tr>
))}
</tbody>
</table>
);
Let's say, I want to have access to index variable of each row in handler.
First solution that comes into mind is to use a closure for capturing index:
container.js:
onClick(e, index) => ({});
presentation.js:
<td onClick={e => onClick(e, index)}>{item.a}</td>
<td>
<div onClick={e => onClick(e, index)}>{item.b}</div>
</td>
It works, but it looks quite dirty, so I decided to find another solution if possible.
The next thing is to use data-index attribute on each element:
container.js:
onClick({ target: { dataset: { index } } }) => ({});
presentation.js:
<td data-index={index} onClick={onClick}>{item.a}</td>
<td>
<div data-index={index} onClick={onClick}>{item.b}</div>
</td>
Maybe it's better, but the need to use data-attribute with same value on multiple elements looks redundant. What if I have 20 elements?
Ok, no problem! Let's just move it to parent container:
container.js:
onClick(e) => {
const {index} = e.target.closest('tr').dataset;
};
presentation.js:
<tr data-index={index}>
<td onClick={onClick}><td>
<td>
<div onClick={onClick}>{item.b}</div>
</td>
</tr>
Ok, the next problem is solved. But wait. This way, container heavily relies on presentational component layout. It becomes coupled to view by introducing reference to tr tag. What if at some point tag will be changed to, for example, div?
The final solution that I come up with is to add a CSS class for container tag element, which is passed from container component to presentation:
container.js:
import Presentation from './Presentation';
import { connect } from 'react-redux';
const ITEM_CONTAINER_CLASS = 'item-cotainer';
export default connect(
({ someState }) => ({ ...someState, ITEM_CONTAINER_CLASS }),
(dispatch) => {
onClick(e) => {
const {index} = e.target.closest(`.${ITEM_CONTAINER_CLASS }`).dataset;
}
}
)(Presentation);
pesentation.js:
export default ({ items, onClick, ITEM_CONTAINER_CLASS }) => (
<table>
<tbody>
{items.map((item, index) => (
<tr className={ITEM_CONTAINER_CLASS}>
<td onClick={onClick}>{item.a}</td>
<td>
<div onClick={onClick}>{item.b}</div>
</td>
</tr>
))}
</tbody>
</table>
);
But still, it has one small disadvantage: it requires a DOM manipulation to get index.
Thanks for reading (if someone). I wonder, if there are another options?
This is classical React/Redux dilemma. My answer would be to extract your "item view" to a component and add an onItemClick prop to it. Of course your item view should receive the item to display, and will be able to send it as a parameter to your container.
The e parameter belongs to your presentation, not to your action creator which is business logic. And storing data in the DOM (like using data- attributes is not needed with React at all).
Something like this (I tend to start from bottom to top, from simple to complex):
//Item.js
export default class Item extends Component {
constructor(props){
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick(e){
e.preventDefault() //this e stays here
this.props.onItemClick(this.props.item)
}
render(){
const { item } = this.props
return (
<tr className={ITEM_CONTAINER_CLASS}>
<td onClick={this.handleClick}>{item.a}</td>
<td onClick={this.handleClick}>{item.b}</td>
</tr>
)
}
}
// ItemList (Presentation)
export default ItemList = ({ items, onClick}) => (
<table>
<tbody>
{
items.map((item, index) =>
<Item item={item} onItemClick={ onClick } />
)
}
</tbody>
</table>
)
// Container.js - just change your mapDispatchToProps to accept the full item, or whatever you're interested in (perhaps only the id)
import ItemList from './ItemList';
import { connect } from 'react-redux';
export default connect(
({ someState}) => ({ ...someState }),
{
onClick(item){
return yourActionCreatorWith(item.id)
}
}
)(Presentation);
That way you have everything you need right when you need to dispatch. In addition, creating your "item view" as a separate component will allow you to test it in isolation and even reuse it if needed.
It may seem much more work but trust me, it pays off.
See also an accepted answer.
Here's the solution:
container.js
import Presentation from './presentation';
import { connect } from 'react-redux';
export default connect(
({ someState }) => ({ ...someState }),
(dispatch) => {
onClick(index) => {}
}
)(Presentation);
presentation.js
import Item from './item.js';
export default ({ items, onClick }) => (
<table>
<tbody>
{items.map((item, index) => <Item index={index} item={item} onClick={onClick} />)}
</tbody>
</table>
);
item.js
export default ({ index, item, onClick }) => {
const onItemClick = () => onClick(index);
return (
<tr>
<td onClick={onItemClick}>{item.a}</td>
<td>
<div onClick={onItemClick}>{item.b}</div>
</td>
</tr>
)
};

How conditionally render mapped object in mapped array

I have two different pieces of data coming into my component, sometimes an array of objects is passed in, and sometimes just an object. My goal is to loop through each object and spit out some JSX. Here is my code:
(Array.isArray(tableData))
?
(tableData.map(obj => {
(Object.keys(obj).map(key => {
return (
<tr>
<td>{key}</td>
<td>{obj[key]}</td>
</tr>
);
}))
}))
:
(Object.keys(tableData).map(key => {
return (
<tr key={key}>
<td>{key}</td>
<td>{tableData[key]}</td>
</tr>
);
}))
You can see im checking to see if the data coming in is an array, and if not loop through just a regular object. That part works fine, but if the data is an array, nothing gets displayed. What is wrong with my code that react doesnt render anything or throw any error messages?
Because you forgot to use return in this line:
(Object.keys(obj).map, try this:
Array.isArray(tableData))
?
tableData.map(obj => {
return Object.keys(obj).map(key => {
return (
<tr>
<td>{key}</td>
<td>{obj[key]}</td>
</tr>
);
})
})
:
Object.keys(tableData).map(key => {
return (
<tr key={key}>
<td>{key}</td>
<td>{tableData[key]}</td>
</tr>
);
})
Assign the unique key to element otherwise you will get a warning.
Mayank's answer solves the problem, but it's a bit verbose. Recall that if you want to return the result of a single expression (e.g. the result of a function call or a JSX element) from an arrow function, you can omit both the curly braces and return:
Array.isArray(tableData)
? tableData.map(obj =>
Object.keys(obj).map(key => (
<tr>
<td>{key}</td>
<td>{obj[key]}</td>
</tr>
)
))
: Object.keys(tableData).map(key => (
<tr key={key}>
<td>{key}</td>
<td>{tableData[key]}</td>
</tr>
))
I've used parentheses above just for clarity.
However, you're repeating the same code here twice, so for simplicity and readability I suggest extracting it into a function of its own:
const tableRows = obj =>
Object.keys(obj).map(key => (
<tr>
<td>{key}</td>
<td>{obj[key]}</td>
</tr>
)
);
// ...
Array.isArray(tableData) ? tableData.map(tableRows) : tableRows(tableData)

Categories

Resources