React button background change on toggle giving error (Using React Hooks) - javascript

I am trying to change/toggle background of a button using inline-styling in React but getting error (in functional component).
Snippet:
import React, { useState } from 'react';
const Home2 = () => {
const [status, setStatus] = useState(false);
btnClick = () => {
setStatus(!status);
}
return (
<div><button onClick={btnClick} style={{background: status? 'orange': 'pink'}}>click 2</button></div>
)
}
export default Home2;
Error:
Kindly help.

You just need to define your btnClick with const.
const btnClick = () => {
setStatus(!status);
}

you need to change a bit your code:
import React, { useState } from 'react';
const Home2 = () => {
const [status, setStatus] = useState(false);
const btnClick = () => {
setStatus(!status);
}
return (
<div>
<button
onClick={btnClick}
style={{background: status? 'orange': 'pink'}}
>
click 2
</button>
</div>
)
}
export default Home2;
You missed const in the function declaration.

You forgot const:
const Home2 = () => {
const [status, setStatus] = React.useState(false);
const btnClick = () => {//you forgot const here
setStatus(status=>!status);//just to be save, use a callback
}
return (
<div><button onClick={btnClick} style={{background: status? 'orange': 'pink'}}>click 2</button></div>
)
}
ReactDOM.render(<Home2 />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

You need to explicitly define the callback function. In classes, we reference callbacks with this. In hooks, this is omitted (luckily) so you have to define a function explicitly.

Because you have not defined the btnClick function. The way it is declared is incorrect. Below should work.
const Home2 = () => {
const [status, setStatus] = useState(false);
const btnClick = () => {
setStatus(!status);
}
return (
<div><button onClick={btnClick} style={{background: status? 'orange': 'pink'}}>click 2</button></div>
)
}
export default Home2;
The way you have declared btnClick is a syntax we follow in class based components. In functional component, you need let or const to declare variables/functions.

Related

React functional component state variable's setFunction is not working when it is called through another function reference

I have found this error while trying to build another React app. So I am only asking the main issue here in a demo app, I might not be able to change any rendering methods here since it is not the actual project.
Issue in simplified form -> I was building a app where two count will be shown and a + button will be there next to that count value. When the button is clicked the count should be increased by 1. Unfortunately when I try to click on the button the value is increasing only the first time. After that the value is not even changing. But when I am implementing the same using Class component its working as expected.
Functional Component
import React, { useState } from "react";
function Page(props) {
const [count, setCount] = useState(0);
const [content, setContent] = useState({
button: (value) => {
return <button onClick={() => handlePlus(value)}>+</button>;
},
});
function handlePlus(value) {
console.log("value=", value);
const data = count + 1;
setCount((count) => data);
}
return (
<div>
<span>Functional Component Count = {count}</span>
{content.button(10)} // 10 will be replaced with another variable
</div>
);
}
export default Page;
Class Component
import React, { Component } from "react";
class PageClass extends Component {
state = {
count: 0,
content: {
button: (value) => {
return (
<button onClick={() => this.handlePlus(value)}>+</button>
);
},
},
};
handlePlus = (value) => {
console.log("value=", value);
this.setState({ count: this.state.count + 1 });
};
render() {
return (
<div>
<span>Class Component Count = {this.state.count}</span>
{this.state.content.button(10)} // 10 will be replaced with another variable
</div>
);
}
}
export default PageClass;
App.js
import "./App.css";
import Page from "./components/Page";
import PageClass from "./components/PageClass";
function App() {
return (
<div className="App">
<Page />
<PageClass />
</div>
);
}
export default App;
However, If I replace that content state variable with normal const variable type and it is working as expected.
Below is working when I am not using any hooks to render the button.
But this is not helpful for my case.
const content = {
content: () => {
console.log(count);
return <button onClick={() => handlePlus(value)}>+</button>;
},
};
I was trying to create some re-usable components and hence I wanted to have that function in state variable which return button tag, so that I can implements some other logic there.
The value will be missing since you're passing a hard-coded 10.
I'd recommend simplifying the handlePlus to just:
setCount(c => c + 1);
Then set the onclick like so:
<button onClick={handlePlus}>+</button>
And your code will work as expected as you can see in this snippet:
const { useState } = React;
const Example = () => {
const [count, setCount] = useState(0);
const [content, setContent] = useState({
content: (value) => {
return <button onClick={handlePlus}>+</button>;
},
});
function handlePlus(value) {
setCount(c => c + 1);
}
return (
<div>
<span>{count}</span>
{content.content(10)}
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
That said, I'd recommend removing the button from the hook, and just render it yourself:
const { useState } = React;
const Example = () => {
const [count, setCount] = useState(0);
function handlePlus(value) {
setCount(c => c + 1);
}
return (
<div>
<span>{count}</span>
<button onClick={handlePlus}>+</button>
</div>
);
}
ReactDOM.render(<Example />, document.getElementById("react"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="react"></div>
See React documentation about the c => c + 1 syntax

make todo and store in localstorage but getting this error fetchdata.map is not a function at App (App.js:27:1)

import React, { useState, useEffect } from 'react';
import './style.css';
export default function App() {
const [state, setState] = useState([]);
const [inputData, setInputData] = useState();
const [fetchdata, setFetchData] = useState([])
const addHandler = () => {
setState((data) => {
return [...data, inputData];
});
localStorage.setItem('state', JSON.stringify(state));
setInputData('');
};
setFetchData(localStorage.getItem('state'))
return (
<div>
<input
onChange={(e) => setInputData(e.target.value)}
value={inputData || ''}
placeholder="add items"
/>
<button onClick={addHandler}>Add</button>
{fetchdata?.map((item) => {
return (
<div style={{ color: `#+${color}` }}>
<li key={item}>{item}</li>
</div>
);
}) || []}
</div>
);
}
This is the code I have tried also need dynamic colors for lists. Any help is appreciated with big thanks
even the key I have given unique but it says unique key required
Try to add a default value to your fetchData:
const [fetchdata, setFetchData] = useState(localStorage.getItem('state') ?? []);
and please don't begin to use useless useEffect like every begginer are doing, further documentation here !
try:
remove
setFetchData(localStorage.getItem('state'))
replace
const initData = () => {
try {
return JSON.parse(localStorage.getItem('state'));
} catch (e) {
return [];
}
}
const [fetchdata, setFetchData] = useState(initData())

How can I send the state (useState) of one file component to another file's component?

REACT.js:
Let say I have a home page with a search bar, and the search bar is a separate component file i'm calling.
The search bar file contains the useState, set to whatever the user selects. How do I pull that state from the search bar and give it to the original home page that
SearchBar is called in?
The SearchBar Code might look something like this..
import React, { useEffect, useState } from 'react'
import {DropdownButton, Dropdown} from 'react-bootstrap';
import axios from 'axios';
const StateSearch = () =>{
const [states, setStates] = useState([])
const [ stateChoice, setStateChoice] = useState("")
useEffect (()=>{
getStates();
},[])
const getStates = async () => {
let response = await axios.get('/states')
setStates(response.data)
}
const populateDropdown = () => {
return states.map((s)=>{
return (
<Dropdown.Item as="button" value={s.name}>{s.name}</Dropdown.Item>
)
})
}
const handleSubmit = (value) => {
setStateChoice(value);
}
return (
<div>
<DropdownButton
onClick={(e) => handleSubmit(e.target.value)}
id="state-dropdown-menu"
title="States"
>
{populateDropdown()}
</DropdownButton>
</div>
)
}
export default StateSearch;
and the home page looks like this
import React, { useContext, useState } from 'react'
import RenderJson from '../components/RenderJson';
import StateSearch from '../components/StateSearch';
import { AuthContext } from '../providers/AuthProvider';
const Home = () => {
const [stateChoice, setStateChoice] = useState('')
const auth = useContext(AuthContext)
console.log(stateChoice)
return(
<div>
<h1>Welcome!</h1>
<h2> Hey there! Glad to see you. Please login to save a route to your prefered locations, or use the finder below to search for your State</h2>
<StateSearch stateChoice={stateChoice} />
</div>
)
};
export default Home;
As you can see, these are two separate files, how do i send the selection the user makes on the search bar as props to the original home page? (or send the state, either one)
You just need to pass one callback into your child.
Homepage
<StateSearch stateChoice={stateChoice} sendSearchResult={value => {
// Your Selected value
}} />
Search bar
const StateSearch = ({ sendSearchResult }) => {
..... // Remaining Code
const handleSubmit = (value) => {
setStateChoice(value);
sendSearchResult(value);
}
You can lift the state up with function you pass via props.
const Home = () => {
const getChoice = (choice) => {
console.log(choice);
}
return <StateSearch stateChoice={stateChoice} giveChoice={getChoice} />
}
const StateSearch = (props) => {
const handleSubmit = (value) => {
props.giveChoice(value);
}
// Remaining code ...
}
Actually there is no need to have stateChoice state in StateSearch component if you are just sending the value up.
Hello and welcome to StackOverflow. I'd recommend using the below structure for an autocomplete search bar. There should be a stateless autocomplete UI component. It should be wrapped into a container that handles the search logic. And finally, pass the value to its parent when the user selects one.
// import { useState, useEffect } from 'react' --> with babel import
const { useState, useEffect } = React // --> with inline script tag
// Autocomplete.jsx
const Autocomplete = ({ onSearch, searchValue, onSelect, suggestionList }) => {
return (
<div>
<input
placeholder="Search!"
value={searchValue}
onChange={({target: { value }}) => onSearch(value)}
/>
<select
value="DEFAULT"
disabled={!suggestionList.length}
onChange={({target: {value}}) => onSelect(value)}
>
<option value="DEFAULT" disabled>Select!</option>
{suggestionList.map(({ id, value }) => (
<option key={id} value={value}>{value}</option>
))}
</select>
</div>
)
}
// SearchBarContainer.jsx
const SearchBarContainer = ({ onSelect }) => {
const [searchValue, setSearchValue] = useState('')
const [suggestionList, setSuggestionList] = useState([])
useEffect(() => {
if (searchValue) {
// some async logic that fetches suggestions based on the search value
setSuggestionList([
{ id: 1, value: `${searchValue} foo` },
{ id: 2, value: `${searchValue} bar` },
])
}
}, [searchValue, setSuggestionList])
return (
<Autocomplete
onSearch={setSearchValue}
searchValue={searchValue}
onSelect={onSelect}
suggestionList={suggestionList}
/>
)
}
// Home.jsx
const Home = ({ children }) => {
const [result, setResult] = useState('')
return (
<div>
<SearchBarContainer onSelect={setResult} />
result: {result}
</div>
)
}
ReactDOM.render(<Home />, document.getElementById('root'))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.9.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.9.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Just pass a setState to component
parent component:
const [state, setState] = useState({
selectedItem: ''
})
<StateSearch state={state} setState={setState} />
change parent state from child component:
const StateSearch = ({ state, setState }) => {
const handleStateChange = (args) => setState({…state, selectedItem:args})
return (...
<button onClick={() => handleStateChange("myItem")}/>
...)
}

Having trouble with react prop validations in JS

this is my first post and I am a beginner with JS and react so go easy. My issue is that I am not getting a number type returned where I want it... it occurs in the CurrentNumber component. Instead it is an [object Object] reference. I am assuming there is something wrong with my types/prop validation. Code posted below.(p.s. I also tried to use the number prop type in the validation) no dice.
import React, {useState,} from "react";
import PropTypes from 'prop-types';
const CurrentNumber = props => (
<p>
{props.countnumber}
</p>
);
const Applet = () => {
// Declaring a state variable called "count"
const [count, setCount] = useState(0);
//########################## Utils & Functions #########################
const incrementCount = props => {
setCount(props.countnumber+=1)
}
const resetCount = () => {
setCount(0);
}
// ######################### View in Browser ####################
return (
<div className={"game"}>
<div className={"header"}>
This is a header.
</div>
<div className={"body"}>
<button onClick={incrementCount}
countnumber = {count}
>
Click me
</button>
<CurrentNumber
countnumber={count}
/>
</div>
<p>
<button onClick = {resetCount}
countnumber = {count}
>
Reset
</button>
</p>
</div>
);
}
///////////// PROP VALIDATIONS ////////////////////
CurrentNumber.propTypes = {
countnumber: PropTypes.string,
}
////////////////////////////////////////////////////////////////////////////
export default Applet;
<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>
const incrementCount = props => {
setCount(props.countnumber+=1)
}
should be
const incrementCount = () => {
setCount(c => c+1)
}
or alternatively
const incrementCount = () => {
setCount(count+1)
}

React Component is not re-rendering on the first state update

I'm currently working on a very simple survey. The kind that you reply by Yes or No. I have made a list of questions that I have put and extracted in its own file (QuestionsList.js).
Here is my question list:
const QuestionsList = [
"Do you believe in ghosts? ",
"Have you ever seen a UFO? ",
"Can cats jump six times their length? "
]
export default QuestionsList
I have my App:
import './App.css';
import Question from './components/Question';
import QuestionsList from './QuestionsList'
import { useState } from 'react';
function App() {
let questionsList = QuestionsList
const [current, setCurrent] = useState(0)
const [currentQuestion, setCurrentQuestion] = useState(null)
const [answers, setAnswers] = useState([])
const [isStarted, setIsStarted] = useState(false)
const onStartHandler = () => {
setIsStarted(true)
updateCurrentQuestion()
}
const updateCurrentQuestion = () => {
setCurrentQuestion(questionsList[current])
}
const onYesHandler = () => {
setCurrent(current => current += 1)
setAnswers([...answers, 1])
updateCurrentQuestion()
}
const onNoHandler = () => {
setCurrent(current => current += 1)
setAnswers([...answers, 0])
updateCurrentQuestion()
}
return (
<div className="App">
{isStarted ? <Question question={currentQuestion} onYes={onYesHandler} onNo={onNoHandler} /> : null}
<button onClick={onStartHandler}>START!</button>
<button onClick={() => console.log(`Current: ${current}\nCurrent Question: ${currentQuestion}\nAnswers: ${answers}`)}>STATE LOG</button>
</div>
);
}
export default App;
And my Question component:
import React from 'react'
const Question = (props) => {
return (
<div>
<h2>{props.question}</h2>
<div>
<button onClick={props.onYes}>YES</button>
<button onClick={props.onNo}>NO</button>
</div>
</div>
)
}
export default Question
The problem is that whenever I launch the app. The first question shows up, but on the very FIRST click on YES or NO, the state changes and so does the question, but the FIRST click, does not rerender the question. However, every subsequent click does re-render the component. What am I missing?
When you call setCurrentQuestion() it uses the previous value of current because setting the state (which setCurrent does) is async.
You don't need the currentQuestion state, because it's derived from current. Use the current value to get the question from the questionsList.
const { useState } = React;
const Question = (props) => {
return (
<div>
<h2>{props.question}</h2>
<div>
<button onClick={props.onYes}>YES</button>
<button onClick={props.onNo}>NO</button>
</div>
</div>
)
}
function App({ questionsList }) {
const [current, setCurrent] = useState(0)
const [answers, setAnswers] = useState([])
const [isStarted, setIsStarted] = useState(false)
const onStartHandler = () => {
setIsStarted(true)
}
const onYesHandler = () => {
setCurrent(current => current += 1)
setAnswers([...answers, 1])
}
const onNoHandler = () => {
setCurrent(current => current += 1)
setAnswers([...answers, 0])
}
return (
<div className="App">
{isStarted ? (
<Question
question={questionsList[current]}
onYes={onYesHandler}
onNo={onNoHandler} />
)
:
(
<button onClick={onStartHandler}>START!</button>
)
}
</div>
);
}
const QuestionsList = [
"Do you believe in ghosts? ",
"Have you ever seen a UFO? ",
"Can cats jump six times their length? "
];
ReactDOM.render(
<App questionsList={QuestionsList} />,
root
);
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
Set initial state
the simple solution:
replace this
const [currentQuestion, setCurrentQuestion] = useState(null)
in this const [currentQuestion, setCurrentQuestion] = useState(questionsList[current])
BTW
I see some things to improve.
the onYesHandler onNoHandler can be one function that get boolean (true or false for yes or no)
You don't need to save the data you import let questionsList = QuestionsList. you can use it from the import as QuestionsList
but it's looks ok for a beginner :)
const updateCurrentQuestion = (num) => {
setCurrentQuestion(questionsList[num])
}
// within no/yes functions
let num = current;
setCurrent(old => old + 1);
updateCurrentQuestion(num);
This should fix it. UpdateCurrentQuestion is getting past state.

Categories

Resources