At this line. I added an onChange Attribute to change the textbox state and update it. But it is not working, I have no idea why because I try the same/ similar thing at other project and it is working correctly. Is there a limit on how many hooks I can use? Is it better to setState as an object with multiple parameters instead of multiple hooks?
<input value={textbox} onChange={e => setTextBox(e.target.value)}></input>
Below is the full code
import React, { useState } from 'react'
export const StartPage = () => {
const [players, SetPlayers] = useState(["jaw", "weihan"])
const [view, setView] = useState(selection)
//const [roles, SetRoles] = useState(null)
const [textbox, setTextBox] = useState("")
const selection = (
<div>
<button onClick={() => setView(addPlayer)}> Create Game </button>
<button> Join Game </button>
</div>
)
const addPlayer = (
<div className="add-player">
<form>
<input value={textbox} onChange={e => setTextBox(e.target.value)}></input>
<button type="submit" > Add more</button>
</form>
<ul>
{players.map(item => <li>{item}</li>)}
</ul>
</div>
)
return (
<section id="start-page">
{view}
</section>
)
}
onChange={e =>{
e.preventDefault()
setTextBox(e.target.value)
}}
Is this you want to achieve?
const [players, SetPlayers] = useState(['jaw', 'weihan']);
const [textbox, setTextBox] = useState('');
const [showPlayersForm, setShowPlayersForm] = useState(false);
const handleSubmit = e => {
e.preventDefault();
SetPlayers(prevState => [...prevState, textbox]);
setTextBox('');
};
const addPlayer = (
<div className='add-player'>
<form onSubmit={handleSubmit}>
<input value={textbox} onChange={e => setTextBox(e.target.value)} />
<button type='submit'> Add more</button>
</form>
<ul>
// use key when you loop trough items
{players.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
</div>
);
return (
<div>
<button onClick={() => setShowPlayersForm(true)}>Create game</button>
<button> Join Game </button>
{showPlayersForm && addPlayer}
</div>
);
Related
I just want to preface this that I am learning JavaScript and React so this is all very new to me.
I am building a "simple" movie rating app and need to be able to push a review to a div "on submit" and cannot figure out how to do so. I have tried using update state in react and/or creating functions to try to accomplish this and cannot figure out how to do this for the life of me. I did somewhat succeed using the latter method, but was getting errors about using unique key props. The other problem was I am to use a star-rating component and when I submitted the review, it wasn't pushing that to the div. This is where I'm at currently:
import { Button, Form, Input } from "reactstrap";
import Stars from "./stars";
export default function ReviewForm() {
const [reviews, setReviews] = useState("");
const onChange = (e: any) => {
setReviews(e.target.value);
};
const onSubmit = (e: any) => {
console.log("Form Submitted");
};
return (
<div className="form-container">
<Stars />
<Form onSubmit={onSubmit}>
<Input
className="form-control" type="text"
placeholder="Enter you review"
value={reviews}
onChange={onChange}
/>
<br></br>
<Button type="submit" className="btn btn-primary">
Submit
</Button>
</Form>
</div>
);
}
// This is what I have in my Stars component:
import React, { useState } from "react";
import { FaStar} from 'react-icons/fa'
const Stars = () => {
const [rating, setRating] = useState(0);
const [hover, setHover] = useState(null);
return(
<div>
{[...Array(5)].map((star, i) => {
const ratingValue = i + 1;
return <label>
<input
type="radio"
name="rating"
value={ratingValue}
onClick={() => setRating(ratingValue)}
/>
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "gold" : "lightgray"}
size={20}
onMouseEnter={() => setHover(ratingValue)}
onMouseLeave={() => setHover(null)}
/>
</label>;
})}
<p>I rate this movie {rating + " stars"}</p>
</div>
);
};
export default Stars```
Here is the working version of your code. You should use key in your map and e.preventDefault() in your form submit function. As final touch you should set another state inside your form submit and show this value in a div or some html element. Also I see that you want to get child state into parent so you can call callback for this https://codesandbox.io/embed/brave-euler-ybp9cx?fontsize=14&hidenavigation=1&theme=dark
ReviewForm.js
export default function ReviewForm() {
const [reviews, setReviews] = useState("");
const [value, setValue] = useState("");
const [star, setStar] = useState();
const onChange = (e: any) => {
setReviews(e.target.value);
};
const onSubmit = (e: any) => {
e.preventDefault();
setValue(reviews + " with " + star + " star ");
};
return (
<div className="form-container">
<Stars setStar={setStar} />
<Form onSubmit={onSubmit}>
<Input
className="form-control"
type="text"
placeholder="Enter you review"
value={reviews}
onChange={onChange}
/>
<br></br>
<Button type="submit" className="btn btn-primary">
Submit
</Button>
<div>{value}</div>
</Form>
</div>
);
}
Stars.js
const Stars = ({ setStar }) => {
const [rating, setRating] = useState(0);
const [hover, setHover] = useState(null);
const handleClick = (ratingValue) => {
setRating(ratingValue);
setStar(ratingValue);
};
return (
<div>
{[...Array(5)].map((star, i) => {
const ratingValue = i + 1;
return (
<label key={i}>
<input
type="radio"
name="rating"
value={ratingValue}
onClick={() => handleClick(ratingValue)}
/>
<FaStar
className="star"
color={ratingValue <= (hover || rating) ? "gold" : "lightgray"}
size={20}
onMouseEnter={() => setHover(ratingValue)}
onMouseLeave={() => setHover(null)}
/>
</label>
);
})}
<p>I rate this movie {rating + " stars"}</p>
</div>
);
};
export default Stars;
You probably are seeing a page refresh when you press the submit button. This is the default behavior of HTML forms.
When using React or any front-end framework, you'd want to handle the form submission yourself rather than letting the browser submit your forms.
In your onSubmit function, add the following line
e.preventDefult()
const onSubmit = (e: any) => {
e.preventDefault()
console.log("Form Submitted");
};
Your code will work perfectly.
import { Button, Form, Input } from "reactstrap";
import Stars from "./stars";
export default function ReviewForm() {
const [Reviews, setReviews] = useState("");
const [ReviewsRating, setReviewsRating] = useState(5);
const [Reviews_, setReviews_] = useState([]);
const onChange = (e: any) => {
setReviews(e.target.value);
};
const onSubmit = (e: any) => {
e.preventDefault()
console.log("Form Submitted");
//After upload to the server
setReviews_([Reviews, ...Reviews_]
};
return (
<div className="form-container">
<Stars getRating={getRating}/>
<Form onSubmit={onSubmit}>
<Input
className="form-control" type="text"
placeholder="Enter you review"
value={reviews}
onChange={onChange}
/>
<br></br>
<Button type="submit" className="btn btn-primary">
Submit
</Button>
</Form>
<div class="reviews">
{Reviews_.map(item => <div> {item}</div> )}
</>
</div>
);
}```
Then to get the stars rating value use props like...
And make sure you call that property (function) inside your Starts component
const getRating =(value)=>{
setReviewsRating(value)
}
Using React & Axios. My post request response is an array of arrays, the second element of nested array is string I am trying to load onto a div through using map. Error is 'undefined is not iterable' I am trying to use useState to use the array outside of post request. The entire section opens with useState via a button and by default is closed/not loaded. There is also a user input which the post request uses to get it data, all of that works fine. I am just unable to map the string from the array into a div. I tried to use window.var to access it but this was unsuccessful as well. Appreciate any help!
import './Turnersdiscoverybody.css'
import axios from 'axios'
import { useState, useEffect } from 'react';
import React, { Component } from 'react'
import Turnershomenav from "../../../components/homepage/homepage-components/Turnershomenav.js";
import orangegtr1 from './turnersdiscovery-images/orangegtr-1.jpg'
import searchicon from './turnersdiscovery-images/searchicon.png'
export default function Turnersdiscoverybody() {
const [showSearchForm, setShowSearchForm] = useState('noForm')
const [input, setInput] = useState['']
//functions for opening and closing search feature
const handleClick = () => {
setShowSearchForm('showForm')
}
const handleClickBack = () => {
setShowSearchForm('noForm')
}
//axios post request starts
//function that handles searching the documents with the user input, using axios
const handleSearch = (e) => {
let userQuery = document.getElementById('userInput').value
e.preventDefault()
axios.post(`http://localhost:8081/getDocumentdata/${userQuery}`)
.then(res => {
setInput(res.data)
console.log(res.data)
})
}
//axios post request ends
return (
<div>
<div className="turnersdiscoverynav">
<Turnershomenav />
</div>
<div className='backgroundimg-container'>
<img src={orangegtr1} alt="background-img" className='turnersdiscovery-backgroundimg'></img>
</div>
{showSearchForm === 'showForm' &&
<>
<img className="img-btn-search" alt="search icon" src={searchicon} onClick={handleClickBack}></img>
<div className='form-search-container'>
<div className='form-search-container-top'>
<input
id="userInput"
required
type="text"
placeholder='enter your query'
></input>
<button onClick={handleSearch}>hello click me for stuff</button>
</div>
<div className='form-search-container-bottom'>
<div className='form-search-container-bottom-content'>
{input.map((data) => (
<div>{data[1]}</div>
)
)}
</div>
</div>
</div>
</>
}
{showSearchForm === "noForm" && <img className="img-btn-search" alt="search icon" src={searchicon} onClick={handleClick}></img>}
</div>
)
}
You have some errors in your code:
//1 => const [input, setInput] = useState([])
//2 => check if inputs is not empty then map!
Now your code must be like this:
export default function Turnersdiscoverybody() {
const [showSearchForm, setShowSearchForm] = useState('noForm')
const [input, setInput] = useState([]);
const [empty, setEmpty] = useState(false);
//functions for opening and closing search feature
const handleClick = () => {
setShowSearchForm('showForm')
}
const handleClickBack = () => {
setShowSearchForm('noForm')
}
//axios post request starts
//function that handles searching the documents with the user input, using axios
const handleSearch = (e) => {
let userQuery = document.getElementById('userInput').value
e.preventDefault()
axios.post(`http://localhost:8081/getDocumentdata/${userQuery}`)
.then(res => {
if(res && res.length > 0){
setInput(res.data)
setEmpty(false)
}else{
setEmpty(true)
}
console.log(res.data)
})
}
return (
<div>
<div className="turnersdiscoverynav">
<Turnershomenav />
</div>
<div className='backgroundimg-container'>
<img src={orangegtr1} alt="background-img"
className='turnersdiscovery-backgroundimg'></img>
</div>
{showSearchForm === 'showForm' &&
<>
<img className="img-btn-search" alt="search icon" src={searchicon} onClick={handleClickBack}></img>
<div className='form-search-container'>
<div className='form-search-container-top'>
<input
id="userInput"
required
type="text"
placeholder='enter your query'
></input>
<button onClick={handleSearch}>hello click me for stuff</button>
</div>
<div className='form-search-container-bottom'>
<div className='form-search-container-bottom-content'>
{empty ? <p>Nothing Found </p> :
<>
{input.length > 0 && input.map((data, index) => (
<div>{data[1]}</div>
)
)}
</>
}
</div>
</div>
</div>
</>
}
{showSearchForm === "noForm" && <img className="img-btn-search" alt="search icon" src={searchicon} onClick={handleClick}></img>}
</div>
)
}
const [input, setInput] = useState['']
should be
const [input, setInput] = useState('')
P.s. if you run into more issues, a minimal code reproduction would be good, somewhere like codepen or codesandbox.
const [input, setInput] = useState['']
should have been
const [input, setInput] = useState([])
Im triying to remove this warning on a react component
Line 19:8: React Hook useEffect has a missing dependency: 'handleChange'. Either include it or remove the dependency array react-hooks/exhaustive-deps
this is the component
const SelectButton = (props)=>{
const [activeState, setActiveState] = useState(false)
const label = props.label
const handleClick = () =>{
setActiveState(!activeState)
//props.handleChange(label,activeState)
}
const handleChange = props.handleChange
useEffect(()=>{
handleChange(label,activeState)
}, [label,activeState])
return(
<button
type="button"
onClick={handleClick}
className={"container-form-button "+(activeState?"active":"")}>
{label}
</button>
)
}
if i tried to remove the comments on handleChange inside of handleClick, handleChange didn´t works correctly
if i tried to change useEffect for something like this
useEffect(()=>{
handleChange(label,activeState)
}, [label,activeState,handleChange])
or
useEffect(()=>{
props.handleChange(label,activeState)
}, [label,activeState,props.handleChange])
it try to reder to many times and throw this error.
Warning: Maximum update depth exceeded. This can happen when a component calls setState inside useEffect, but useEffect either doesn't have a dependency array, or one of the dependencies changes on every render
Actually it works like first code, but im still having the warning
this is the parent original handleChange
const handleChange = (name,value)=>{
setSelected({...selected, [name]:value})
}
the parentComponent
const CategorySearcher = (props) =>{
const ciudades = ["Guadalajara","Zapopan","Tlajomulco"]
const tipos = ["Casa","Departamento"]
const [selected, setSelected] = useState({})
const handleChange = useCallback(
(label,value) => {
setSelected({...selected, [label]:value})
},
[selected],
)
useEffect(() => {
console.log(selected);
}, [selected])
const cities = ciudades.map((city)=><SelectButton key={city} handleChange={handleChange} label={city}/>)
const types = tipos.map((tipo)=><SelectButton key={tipo} handleChange={handleChange} label={tipo}/>)
let history = useHistory();
const setUrlSearch = ()=>{
let urlSearch = "search/q="
let attToSearch = []
for (var key in selected) {
selected[key]?attToSearch.push(key):console.log("Nada")
}
/*
attToSearch.forEach((it)=>{
urlSearch = urlSearch+"&"+it
})*/
console.log(urlSearch);
history.push(urlSearch+attToSearch)
}
return (
<section className="general-container">
<div className="container-box">
<span className="container_title">
Tu nuevo hogar esta aquí :)
</span>
</div>
<div className="container-form">
<form className="container-form-box">
<div className="container-form-cities">
<div className="container-form-subtitle">
Ciudades
</div>
<div className="container-buttons">
{cities}
</div>
</div>
<div className="container-form-cities">
<div className="container-form-subtitle">Tipo de hogar</div>
<div className="container-buttons">
{types}
</div>
</div>
<div className="container-box-button">
<button className="button-form-CTA" onClick={setUrlSearch}>
Buscar
</button>
</div>
</form>
</div>
</section>
)
}
You should be wrapping your function with the useCallback hook before passing it as a prop. Documentation can be found here.
You shouldn't be using useEffect like that.
const SelectButton = ({ label, handleChange }) => {
const [activeState, setActiveState] = React.useState(false);
const handleClick = () => {
const newState = !activeState
setActiveState(newState);
handleChange(label, newState);
};
return (
<button
type="button"
onClick={handleClick}
className={"container-form-button " + (activeState ? "active" : "")}
>
{label}
</button>
);
};
Why don't you destructure label and handleChange from props? Check out this sandbox, this does not cause any infinite update loop: codesandbox
import React from "react";
export default function App() {
const handleChange = () => console.log("handling change");
return (
<div className="App">
<SelectButton label="Label" handleChange={handleChange} />
</div>
);
}
const SelectButton = ({ label, handleChange }) => {
const [activeState, setActiveState] = React.useState(false);
const handleClick = () => {
setActiveState(!activeState);
};
React.useEffect(() => {
handleChange(label, activeState);
}, [label, activeState, handleChange]);
return (
<button
type="button"
onClick={handleClick}
className={"container-form-button " + (activeState ? "active" : "")}
>
{label}
</button>
);
};
I am having a hard time rendering components conditionally in React. I have successfully rendered 2 components (A and B) conditionally but couldn't find any successful way to add a third component (C) in our case
this is the code for 2 componnets:
function App() {
const [click, setClick] = useState(true);
const ShowA = () => setClick(true);
const ShowB = () => setClick(false);
return (
<>
<br />
<button onClick={ShowA}>A </button>
<button onClick={ShowB}>B </button>
<div className="App">
{click && <div> A </div>}
{!click && <div>B</div>}
</div>
</>
);
}
Is there any possible way I can add a third C component so I can toggle between them? I have been trying for 2 days but no success.
This is the link of Codesandbox if anyone's interested
https://codesandbox.io/s/musing-tesla-9gkpw?file=/src/index.js:100-481
You can put as many states as you want:
function App() {
const [displayA, setDisplayA] = useState(true);
const [displayB, setDisplayB] = useState(true);
const [displayC, setDisplayC] = useState(true);
const showA = () => {
setDisplayA(true);
setDisplayB(false);
setDisplayC(false);
}
const showB = () => {
setDisplayA(false);
setDisplayB(true);
setDisplayC(false);
};
const showC = () => {
setDisplayA(false);
setDisplayB(false);
setDisplayC(true);
};
return (
<>
<br />
<button onClick={showA}>A</button>
<button onClick={showB}>B</button>
<button onClick={showC}>C</button>
<div className="App">
{displayA && <div>A</div>}
{displayB && <div>B</div>}
{displayC && <div>C</div>}
</div>
</>
);
}
And you can even put other things in your state, like JSX elements:
function App() {
const [elementToDisplay, setElementToDisplay] = useState("");
const showA = () => {
setElementToDisplay(<div>A</div>)
}
const showB = () => {
setElementToDisplay(<div>B</div>)
}
const showC = () => {
setElementToDisplay(<div>C</div>)
}
return (
<>
<br />
<button onClick={showA}>A</button>
<button onClick={showB}>B</button>
<button onClick={showC}>C</button>
<div className="App">
{elementToDisplay}
</div>
</>
);
}
You can save a state for the current button, and then show the different button conditionally using object lookup:
Check https://codesandbox.io/s/kind-haslett-b0fv0
function App() {
const [currentButton, setCurrentButton] = useState('A');
return (
<>
<br />
<button onClick={() => setCurrentButton('A')}>A</button>
<button onClick={() => setCurrentButton('B')}>B</button>
<button onClick={() => setCurrentButton('C')}>C</button>
<div className="App">
{
({
A: <div>A</div>,
B: <div>B</div>,
C: <div>C</div>
})[currentButton]
}
</div>
</>
);
}
Trying to populate a list from an API call/search on a react app. Getting the error that property "map" is undefined in Main.js where the movieList is being called. Application runs until you try and type into the search box. Any clues as to what I'm doing wrong here??
App JS:
function App() {
const [movieList, SetMovieList] = useState([]);
const [search, SetSearch] = useState("");
const HandleSearch = e => {
e.preventDefault();
FetchMovies(search);
}
const FetchMovies = async (query) => {
const temp = await fetch(`http://www.omdbapi.com/=${query}`)
.then(res => res.json());
SetMovieList(temp.results);
}
<div className="content-wrap">
<Sidebar nominations={nominations}/>
<Main
HandleSearch={HandleSearch}
search={search}
SetSearch={SetSearch}
movieList={movieList}
/>
</div>
Main JS:
<main>
<div className="main-head">
<form className="search-box"
onChange={props.HandleSearch}>
<input
type="search"
placeholder="Search the OMDB database..."
required
value={props.search}
onSubmit={e => props.SetSearch(e.target.value)}/>
</form>
</div>
<div className="movie-list">
{props.movieList.map(movie => (
<MovieCard
movie={movie}
key={movie.mal_id} />
))}
</div>
</main>
It's because the movieList is undefined or empty.
Try to check whether it has data or not with conditional operator:
<div className="movie-list">
{props.movieList ? props.movieList.map(movie => (
<MovieCard
movie={movie}
key={movie.mal_id} />
)) : {}
}
</div>