ReactJs can't fix missing semicolon error before "render()" - reactjs

I was following ProgrammingWithMosh's reactJs tutorial, and I am now stuck in the App.js file (I have other children components in my project too, but I have not posted their code in this question) due to the following error:
';' expected. [Line 41 of the following code]
This error occurs on the line where "render()" is found - right after the "render()" keyword and before its first "{" bracket.
Here is the code:
import React, { Component } from 'react';
import NavBar from "./components/navbar";
import Counters from './components/counters';
import './App.css';
function App() {
state = {
counters: [
{ id: 1, value: 4 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 },
],
};
handleReset = () => {
const counters = this.state.counters.map((c) => {
c.value = 0;
return c;
});
this.setState({ counters: counters });
};
handleIncrement = (counter) => {
// console.log(counter);
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
// counters[index] = { ...counter };
counters[index].value++;
this.setState({ counters: counters });
// console.log(this.state.counters[index]);
};
handleDelete = (counterId) => {
// console.log("Event Handler Called", counterId);
const counters = this.state.counters.filter((c) => c.id !== counterId);
// console.log(counters);
this.setState({ counters: counters });
};
render() {
return (
<React.Fragment>
<NavBar />
<main className="container">
<Counters
counters={this.state.counters}
onReset={this.handleReset}
onIncrement={this.handleIncrement}
onDelete={this.handleDelete}
/>
</main>
</React.Fragment>
);
}
}
export default App;
I cannot figure out what semi colons I am missing and I have also checked to see if I am missing any ending brackets from previous functions (which to my knowledge, I am not). Please help me pinpoint the issue, and thank you so much in advance!

You're mixing a function component with a class component.
change
function App() {
to
class App extends Component {

Related

Undefined state in parent when setting state in child - React

I am trying to disable a button onClick based on if the button is the "current player". I am able to successfully get the correct code in the console log from my PlayerButton component, but I also get errors from my App.js that .map is not a function, meaning that my playerData (which works on initial mount) becomes undefined somehow after the onClick (i also receive undefined when trying to log playerData in App.js after the onClick).
Any ideas as to why my setPlayerData function isn't passing the data back to the parent component? I am thinking I may need to either use async await or useEffect somewhere, but I'm not sure how to implement it in this case.
App.js
import React, { useState } from 'react';
import './app.css'
import PlayerButton from './components/PlayerButton';
function App() {
const [playerData, setPlayerData] = useState([
{
name: "Player 1",
currentPlayer: true,
position: 0
},
{
name: "Player 2",
currentPlayer: false,
position: 0
},
{
name: "Player 3",
currentPlayer: false,
position: 0
}
]);
return (
<div className="container">
{playerData.map((player, index) => (
<div key={player.name}>
<PlayerButton player={player} index={index} setPlayerData={setPlayerData} />
</div>
))}
</div>
);
}
export default App;
PlayerButton.js
import React from 'react';
const PlayerButton = ({ player: { name, currentPlayer }, index, setPlayerData }) => {
const handleButtonClick = () => {
setPlayerData(playerData => {
playerData.map(player => {
if (player.name === name) {
console.log({ ...player, currentPlayer: false })
return { ...player, currentPlayer: false }
}
return player
})
})
}
return (
<button disabled={!currentPlayer} type="button" id={index + 1} className={`player${index + 1}-btn`} onClick={handleButtonClick}>{name}</button>
)
}
export default PlayerButton;
I experimented with useEffect and async await because, I'm assuming, on update the parent component is trying to re-render before the data is successfully passed from the child. But if that were the case, wouldn't it just re-render the previous data?
Somehow, I'm not successfully updating the state in the child so the parent can use it.
You've lost a return statement and returned undefined instead
Should be:
setPlayerData(playerData => {
return playerData.map(player => {
if (player.name === name) {
console.log({ ...player, currentPlayer: false })
return { ...player, currentPlayer: false }
}
return player
})
})
Ok I found a solution. I created a function in the parent component that uses the setPlayerData function inside a callback setCurrentPlayer, passed it to the child, and executed the callback inside the child.
App.js
const setCurrentPlayer = () => {
setPlayerData((playerData.map((player) => {
if (player.currentPlayer) {
return { ...player, currentPlayer: false}
}
return player
})))
}
PlayerButton.js
const handleButtonClick = () => {
setCurrentPlayer();
}

Converting a React Class Component to Function Component

I have build a small application using class component and it's working fine also.
While I convert that application to function component to use react hooks, it's giving me error.
Please check and let me know where it went wrong.
function component
import React ,{useState} from 'react';
import './App.css';
import Counters from './component/counters';
import Navbar from './component/navbar';
function App(props) {
const initialState = [
{ id: 1, value: 0 },
{ id: 2, value: 10 },
{ id: 3, value: 20 },
{ id: 4, value: 30 },
];
const [counters, setCounters] = useState(initialState);
const handleIncrement = (counter) => {
// const counters = [...counters];
// const index = counters.indexOf(counter);
// counters[index] = { ...counter };
// counters[index].value++;
setCounters({ counter : counter.value +1 });
};
const handleDecrement = (counter) => {
// // const counters = [...counters];
// const index = counters.indexOf(counter);
// counters[index] = { ...counter };
// counters[index].value--;
// setCounters({ counters });
};
const handleDelete = (counterId) => {
// const counters = counters.filter((c) => c.id !== counterId);
// setCounters({ counters });
};
return (
<div className="container">
{/* <Navbar totalCounters={counters.reduce((a,c)=>a + c.value,0 )}/> */}
<Navbar totalCounters={counters.filter((c) => c.value > 0).count()}/>
<Counters
counters={counters}
onIncrement={handleIncrement}
onDecrement={handleDecrement}
onDelete={handleDelete}
/>
</div>
);
}
export default App;
class component
import './App.css';
import Counters from './component/counters';
import Navbar from './component/navbar';
import React from 'react';
class App extends React.Component {
state = {
counters: [
{ id: 1, value: 0 },
{ id: 2, value: 10 },
{ id: 3, value: 20 },
{ id: 4, value: 30 },
],
};
handleIncrement = (counter) => {
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = { ...counter };
counters[index].value++;
this.setState({ counters });
};
handleDecrement = (counter) => {
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = { ...counter };
counters[index].value--;
this.setState({ counters });
};
handleDelete = (counterId) => {
const counters = this.state.counters.filter((c) => c.id !== counterId);
this.setState({ counters });
};
render() {
return (
<div className="container">
<Navbar totalCounters={this.state.counters.reduce((a,c)=>a + c.value,0)}/>
<Counters
counters={this.state.counters}
onIncrement={this.handleIncrement}
onDecrement={this.handleDecrement}
onDelete={this.handleDelete} />
</div>
);
}
}
export default App;
In case, if you want to see the full code then just let me know.
Issue
The issue is your counters state is an array but you are mutating the state invariant to be an object in your handlers.
Solution
App - Use a functional state update to map the counters array from the previous state to the next state, using the id to match the specific counter you want to increment/decrement/delete. This shallow copies the previous array. Notice also that the counter being updated is also shallow copied into a new object reference. When deleting a counter you are correct to use .filter to remove the specific element and return a new array.
function App(props) {
const initialState = [
{ id: 1, value: 0 },
{ id: 2, value: 10 },
{ id: 3, value: 20 },
{ id: 4, value: 30 },
];
const [counters, setCounters] = useState(initialState);
const handleIncrement = (id) => {
setCounters(counters => counters.map(counter => counter.id === id
? {
...counter,
value: counter.value + 1,
}
: counter
));
};
const handleDecrement = (id) => {
setCounters(counters => counters.map(counter => counter.id === id
? {
...counter,
value: counter.value - 1,
}
: counter
));
};
const handleDelete = (id) => {
setCounters(counters => counters.filter((c) => c.id !== id));
};
return (
<div className="container">
<Navbar totalCounters={counters.filter((c) => c.value > 0).count()}/>
<Counters
counters={counters}
onIncrement={handleIncrement}
onDecrement={handleDecrement}
onDelete={handleDelete}
/>
</div>
);
}
Counters - pass the counter id to the handlers
class Counters extends Component {
render() {
const { onIncrement, onDecrement, onDelete, counters } = this.props;
return (
<div>
{counters.map((counter) => (
<Counter
key={counter.id}
counter={counter}
onIncrement={() => {
onIncrement(counter.id);
}}
onDecrement={() => {
onDecrement(counter.id);
}}
onDelete={() => {
onDelete(counter.id);
}}
/>
))}
</div>
);
}
}
Counter - since the id is closed in callback scope above just call the event handlers
class Counter extends Component {
...
render() {
return (
<div>
<span className={this.getBadge()}>{this.getCount()}</span>
<button
className="btn btn-secondary m-2"
onClick={this.props.onIncrement}
>
Increment
</button>
<button
className="btn btn-secondary m-2"
onClick={this.props.onDecrement}
>
Decrement
</button>
<button className="btn btn-danger m-2" onClick={this.props.onDelete}>
Delete
</button>
</div>
);
}
}
In your handleIncrement() function, you're setting an object, when the setCounters method is expecting an array of objects.
You can use ES6 destructuring. Also note the object key should be the id not the object.
I expect the below to fix the error.
setCounters([...counters, { counter.id : counter.value +1 }]);
And I believe below is the implementation you're looking for.
otherCounters = counters.filter(c => c.id != counter.id)
setCounters([...otherCounters, { counter.id : counter.value +1 }])
EDIT: One thing I noticed in your counters.jsx and counter.jsx is that you're calling a function with variables that do not expect one.
From counters.jsx
<Counter
...
// onIncrement of "Counter" (the 1st onIncrement on the left)
// is a function that do not expect a variable.
onIncrement={() => {onIncrement(counter);}}
/>
From counter.jsx
<button>
...
// "onIncrement" is called with "this.props.counter". However this "onIncrement" is not expecting a variable.
onClick={() => this.props.onIncrement(this.props.counter)}
</button>
For the correct syntax, you can change either, but as example you can change counter.jsx to
<button>
...
onClick={() => this.props.onIncrement()}
</button>
In functional component on react when you wanna make state you must be use useState, you can follow my code here:
import './App.css';
import Counters from './component/counters';
import Navbar from './component/navbar';
import { useState } from 'react';
const App = () => {
const [countersState, setCountersState] = useState([
{ id: 1, value: 0 },
{ id: 2, value: 10 },
{ id: 3, value: 20 },
{ id: 4, value: 30 },
]);
const handleIncrement = (counter) => {
const counters = countersState
const index = counters.indexOf(counter);
counters[index] = { ...counter };
setCountersState(counters);
};
const handleDecrement = (counter) => {
const counters = countersState
const index = counters.indexOf(counter);
console.log(index)
counters[index] = { ...counter };
counters[index].value--;
setCountersState(counters);
};
const handleDelete = (counterId) => {
const counters = countersState.filter((c) => c.id !== counterId);
setCountersState(counters)
}
return (
<div className="container">
<Navbar totalCounters={this.state.counters.reduce((a,c)=>a + c.value,0)}/>
<Counters
counters={countersState}
onIncrement={handleIncrement}
onDecrement={handleDecrement}
onDelete={handleDelete} />
</div>
);
};
export default App;

Invalid hook call. ReacJs

I have been troubleshooting this error for hours now.
I'm using the hooks on top of a function (getItems). i don't know what mistake i have done.
How should i clear this?
ERROR:
`
Error: Invalid hook call. Hooks can only be called inside of the body
of a function
component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug
and fix this problem.
`
App.js
import React from "react";
const data = {
list: [
{
id: 0,
title: "A1",
list: [
{
id: 3,
title: "A2",
},
]
},
]
};
function getItems() {
const [menuStack, setStack] = React.useState([data.list]);
const pushState = (list) => {
list && setStack((stack) => [...stack, list]);
};
const popState = () => {
menuStack.length > 1 && setStack((stack) => stack.slice(0, -1));
};
const top = menuStack[menuStack.length - 1];
return (
<button onClick={popState}>BACK</button>
);
}
export default class PopUp extends React.Component {
render() {
return (
<div>
{getItems()}
</div>
);
}
}
Index.js
import React from "react";
export default class Home extends React.Component {
render(){
return (
<App />
);
}
}
You are calling function getItems inside your PopUp component and expecting to print the output. React treat that function as a normal function and hence throws that error.
You need to tell react that this is a component function by doing
<div>
<getItems />
</div>
The issue is with class getItems, You can use that as a Component. <GetItems />
import React from "react";
const data = {
list: [
{
id: 0,
title: "A1",
list: [
{
id: 3,
title: "A2",
},
],
},
],
};
function GetItems() {
const [menuStack, setStack] = React.useState([data.list]);
const pushState = (list) => {
list && setStack((stack) => [...stack, list]);
};
const popState = () => {
menuStack.length > 1 && setStack((stack) => stack.slice(0, -1));
};
const top = menuStack[menuStack.length - 1];
return <button onClick={popState}>BACK</button>;
}
export default class PopUp extends React.Component {
render() {
return (
<div>
<GetItems />
</div>
);
}
}
In react, hooks can be written in functional components. But you called getItems function as a general function, not a functional component.
You should let react treat as a component like following:
<getItems />

React hooks: How do I update state with object

I have 3 components like this, how I update state in App component
How I update state onclick in Counter component
import React, { useState } from 'react'
import Header from './components/Header'
import Counters from './components/Counters'
const App = () => {
const initialCounters = [
{ id: 1, value: 0 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 },
]
const [counters, setCounters] = useState(initialCounters)
const onIncrement = (counter) => {
console.log(counter)
}
return (
<>
<Header totalCounters={counters.length} />
<main className='container'>
<Counters counters={counters} onIncrement={onIncrement} />
</main>
</>
)
}
export default App
In your Counters component when you call the OnIncrement method you need to pass it's id as reference.
Then in your OnIncrement you do this
const onIncrement = (counterId) => {
const updatededCounters = counters.map((counter) => {
if(counter.id === counterId){
counter.value++
return counter
}
return counter
})
setCounters(updatededCounters)
}
Just to be clear in your Counters component
import React from "react";
const Counters = ({ counters, onIncrement }) => {
return (
<>
{counters.map((counter) => (
<div key={counter.id}>
<p>My counter : {counter.value}</p>
<button onClick={() => onIncrement(counter.id)}>Increment</button>
</div>
))}
</>
);
};
Full code for the parent component
import React, { useState } from "react";
import Counters from "./Counters";
const App = () => {
const initialCounters = [
{ id: 1, value: 0 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 }
];
const [counters, setCounters] = useState(initialCounters);
const onIncrement = (counterId) => {
const updatededCounters = counters.map((counter) => {
if (counter.id === counterId) {
counter.value++;
return counter;
}
return counter;
});
setCounters(updatededCounters);
};
return (
<>
<main className="container">
<Counters counters={counters} onIncrement={onIncrement} />
</main>
</>
);
};
export default App;
Codesandbox link: https://codesandbox.io/s/sad-chebyshev-l4hez?file=/src/App.js:0-725
Explanation
What i do the onIncrement method is simple:
i will create a new array with values i want to edit inside of it, then i'll set the state with this new array.
In the .map()
Each instance of counter will be looped, so for each instance i check if one of them is the one im looking for to update (with the same id as the counterId i receive as parameter)
If so, i edit the value of the current counter and then return the counter instance to allow the loop to continue to the next one.
When the counter id is not the same as the counterId received, i just return the counter without any modification.
So at the end, you will get the same array of values, exepct for the specific counter you incremented where you will see it's value updated by one count
I advice you to read some documentation about .map() function since it's really used in react : https://fr.reactjs.org/docs/lists-and-keys.html
And aswell you coud look into Object.keys() beceause it's often used with .map() aswell if you need to loop through object properties : https://reedbarger.com/how-to-transform-javascript-objects-the-power-of-objectkeys-values-entries/

React JS, how to show state in two pages

I am currently developing a website for counting points, the user can give points to each player. The total of all points are calculated and shown below the counters on the administrator page. Now what I want is that I also want to show the total value in a different page, where players can see how much points their team has scored. But this total value has to be in sync with the total value in the administrator page. How can I do this? I heard about axios, but have no idea how this works. Can someone help me?
My Code:
Counter
import React, { Component } from "react";
import ReactDOM from "react-dom";
export default class Counter extends Component {
render() {
const { onIncrement, onDecrement } = this.props;
return (
<div>
<span>{this.formatCount()}</span>
<button onClick={() => onIncrement(this.props.counter)}>
Add
</button>
<button
onClick={() => onDecrement(this.props.counter)}
disabled={this.props.counter.value === 0 ? "disabled" : ""}
>
Delete
</button>
</div>
);
}
formatCount() {
const { value } = this.props.counter;
return value;
}
}
if (document.getElementById("counter")) {
ReactDOM.render(<Counter />, document.getElementById("counter"));
}
Counters
import React, { Component } from "react";
import ReactDOM from "react-dom";
import Counter from "./counter";
class Counters extends Component {
constructor() {
super();
this.state = {
counters: [
{ id: 1, value: 0 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 },
{ id: 5, value: 0 }
],
total: 0
};
}
handleIncrement(counter) {
const total = this.state.total + 1;
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = { ...counter };
counters[index].value++;
this.setState({ counters: counters, total: total });
}
handleDecrement(counter) {
const total = this.state.total - 1;
const counters = [...this.state.counters];
const index = counters.indexOf(counter);
counters[index] = { ...counter };
counters[index].value--;
this.setState({ counters: counters, total: total });
}
handleReset() {
const total = 0;
const counters = this.state.counters.map(c => {
c.value = 0;
return c;
});
this.setState({ counters: counters, total: total });
}
render() {
return (
<div>
<button onClick={this.handleReset.bind(this)}>Reset</button>
{this.state.counters.map(counter => (
<Counter
key={counter.id}
onIncrement={this.handleIncrement.bind(this)}
onDecrement={this.handleDecrement.bind(this)}
counter={counter}
/>
))}
<span>{this.state.total}</span>
</div>
);
}
}
export default Counters;
if (document.getElementById("counters")) {
ReactDOM.render(<Counters />, document.getElementById("counters"));
}
Axios is an http client so that wouldn't apply to your problem.
Theres multiple solutions to your problem.
Option #1: Callbacks
Depending on how your admin and non-admin page is structured/nested this option might be easy or difficult to implement.
You could have a parent component that holds all your counter value data and renders either your admin or non-admin component that shows your counters.
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
counters: [
{ id: 1, value: 0 },
{ id: 2, value: 0 },
{ id: 3, value: 0 },
{ id: 4, value: 0 },
{ id: 5, value: 0 }
],
total: 0
};
}
...
render() {
return (
<Switch>
<Route exact path="/admin" render={() => <Admin counters={this.state.counters} total={this.state.total} />}/>
<Route exact path="/user" render={() => <Admin counters={this.state.counters} total={this.state.total} />}/>
</Switch>
);
}
}
This will keep in sync because the counters are only being stored in one place, the Parent component.
Option #2: Global state management library
The alternative is a state management library like Redux. This gives you a global state that does not get deleted when a component is unmounted unlike your local this.state. You would put counters into this global state then your admin would perform actions on it, like increment and decrement, and admin and non-admin components would fetch counter values from it similar to local state by calling this.props.reduxStateCounters, for example.
More can be read about Redux here.

Resources