I have a component which is a select menu and I get the value selected from the select menu when it changes and then both values from the 2 select menus when I click the button but the value appears as [object Object] from the console and then I attached a screenshot of the alert output.
Can anyone see why from my code the values are not as expected in my Replay component as the values display properly on the actually menu it just doesn't seem to be the correct value in the Replay component?
screenshot of alert:
First component which renders the 2 select menus and button:
function Replay() {
const [firstActivity, setFirstActivity] = useState();
const [secondActivity, setSecondActivity] = useState();
const handleFirstChange = (value) => {
console.log("evt.target = ",(value));
setFirstActivity(value.name);
}
const handleSecondChange = (value) => {
setSecondActivity(value.name);
}
const handleClick = () => {
alert("values select are: ", firstActivity, secondActivity);
}
return (
<div className='top-container'>
<button onClick={handleClick}>Get select values!</button>
<div className='container'>
<Menu
onChange={handleFirstChange}
/>
<Menu
onChange={handleSecondChange}
/>
</div>
</div>
)
}
export default Replay;
2nd select menu component:
import React, {useState, useContext} from 'react';
import { GlobalContext } from "./Store";
function Menu(props) {
const [activities] = useContext(GlobalContext);
const handleMenuChange = (evt) => {
console.log("evt.target.value = ", evt.target.value);
props.onChange(evt.target.value);
}
const createOptions =
<>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</>
return (
<div className="container">
<select
className="select"
defaultValue="1"
onChange={handleMenuChange}
>
{createOptions}
</select>
</div>
);
}
export default Menu;
First Problem:
alert doesn't take multiple arguments. This is one of the many reasons not to use it for debugging. Use console.log instead:
console.log("values select are: ", firstActivity, secondActivity);
Second Problem:
You're passing the value to your change handler function(s):
props.onChange(evt.target.value);
A value which you just logged as a plain value and observed that it's just a number:
console.log("evt.target.value = ", evt.target.value);
Then in that handler function you again observe that the value passed to it is just a number (or at least a numeric string):
console.log("evt.target = ",(value));
Then what do you try to do with that value? This:
setFirstActivity(value.name);
Why do you expect it to have a name property? Where did that property come from? It's just a plain value. Use the value:
setFirstActivity(value);
Example
Related
I am working on Reactjs and using nextjs framework,Right now i have dropdown and i just want to redirect to another url/page
onclick, So for this i tried with following code but not working,Where i am wrong ?
|
import { useRouter } from 'next/router'
const Footer = () => {
const router = useRouter()
const handleClick = (e) => {
e.preventDefault()
router.push('/some-path')
}
...
}
<select className="form-select mb-3" aria-label="Default select example">
<option selected>English</option>
<option value="1" onClick={handleClick}>Arabic - Coming Soon</option>
</select>
The onClick event is not supported on the option element in HTML. Instead, you should attach the onChange event to the select element and update the router based on the selected option. Here's an example:
import { useRouter } from 'next/router'
const Footer = () => {
const router = useRouter()
const handleChange = (event) => {
const selectedValue = event.target.value
if (selectedValue === '1') {
router.push('/some-path')
}
}
return (
<select className="form-select mb-3" aria-label="Default select example" onChange={handleChange}>
<option selected>English</option>
<option value="1">Arabic - Coming Soon</option>
</select>
)
}
In this example, we've attached the onChange event to the select element and passed it the handleChange function. In the handleChange function, we get the value of the selected option and check if it's equal to "1". If it is, we use the router.push() function to navigate to the desired URL.
I have an array with several "question" elements, each of them with a structure similar to this:
<><div className="row"><div className="col-12 col-md-9 col-lg-10 col-xl-10 col-xxl-10"><span>Question 1</span></div><div className="col-3 col-md-1 col-lg-1 col-xl-1"><div className="form-check"><input id="formCheck-1" className="form-check-input" type="checkbox" /><label className="form-check-label" for="formCheck-1">Yes</label></div></div><div className="col-3 col-md-1 col-lg-1 col-xl-1"><div className="form-check"><input id="formCheck-2" className="form-check-input" type="checkbox" /><label className="form-check-label" for="formCheck-2">No</label></div></div></div></>,
In order to give each element a bit of keyed structure, I store each array element in a helper component. The HTML of the questions is simply stored in "element". Simple:
const ElementoPaginacion = ({element}) =>{
return(
element
)
}
Since there can be many of these elements in this array, they are displayed with pagination. The displayed page is calculated (apparently correctly, using a simple calculation). The code snippet that calculates and displays it is as follows:
<>
{
//Calculate init index (it depends ont the current page) to show the questions, and the number of elements to show (its rangePages)
fullList.slice(currentPage * rangePages, (currentPage * rangePages) + rangePages).map((current) => (
<React.Fragment key={current.key}>
{current}
</React.Fragment>
))
}
What happens is that, when a change is made to that HTML by the user (for example, checking a checkbox), when changing the page, that change is NOT saved, if it is redrawed (for example, changing the page and returning to the same page afterwards). I am attaching images to see how it works:
We can see how we make changes to the questions on page 0, we change to page 1, and when we return to page 0 again, the changes (check ckeckboxes) have not been saved.
That could be happening?
------------- EDIT -------------
Okay. Right now, the idea is to save changes to any portion of HTML that is passed to us, be it a question with checkbox responses, or a radiobutton.
The problem: if we don't know what content is going to pass, what we have to save is all the content in html. Now, how can we save any HTML content that has been modified? I've tried creating this helper component, which wraps the HTML content passed to it inside a "div", but when clicked, how can I retrieve the new HTML content to reassign (ie the "newData" parameter)?
const ElementoPaginacion = ({element}) =>{
const [content, saveElement] = useState(element);
const saveData = (newData) =>{
saveElement(newData);
}
return(
<div onChange={saveData}>
{element}
</div>
)
}
Are you using a backend database? If not, the changes are stored in memory and will be lost each time you change the page.
You can use the useState hook to prevent the state of the forms from being lost each time the element is dismounted. Then the user can submit the state in its entirety after answering the questions.
Example:
import { useState } from 'react';
import { Button } from '#mui/material';
import { Checkbox } from '#mui/material';
function App() {
const [checkBoxIsMounted, setCheckBoxIsMounted] = useState(false);
const handleClick = () => {
setCheckBoxIsMounted(!checkBoxIsMounted)
}
return (
<>
<Button onClick={handleClick}>Mount Checkbox</Button>
{checkBoxIsMounted && <Checkbox />}
</>
);
}
export default App;
Note, I'm using MUI instead of Bootstrap, but the underlying principle is the same.
The above code snippet produces the following behavior:
If we add state to the checkbox, React will maintain the state in memory even after the component is dismounted:
import { useState } from 'react';
import { Button } from '#mui/material';
import { Checkbox } from '#mui/material';
function App() {
const [checkBoxIsMounted, setCheckBoxIsMounted] = useState(false);
const [checked, setChecked] = useState(false);
const handleClick = () => {
setCheckBoxIsMounted(!checkBoxIsMounted)
}
const handleChange = () => {
setChecked(!checked)
}
return (
<>
<Button onClick={handleClick}>Mount Checkbox</Button>
{checkBoxIsMounted && <Checkbox onChange={handleChange} checked={checked} />}
</>
);
}
export default App;
This modified snippet produces this behavior:
Could you please tell me How to add items in select dynamically in react ?
I am getting response from server (name , label etc).For example I just mock my response and after two second I fetch that .
In my example I have two select box or drop down .first dropdown have value “one” and “two”(which is given as options in json).
In json there is one more option dependentField it mean the field is dependent on another field (value mentioned is dependent).In my example second field is dependent on first field.
So the value of second select or dropdown field will be ["three", "four"] if first select box or dropdown value id one.
So the value of second select or dropdown field will be ["five", "six"] if first select box or dropdown value id two.
So I need to watch the value of field as mention in hook form
https://react-hook-form.com/get-started
Can we dynamically add options
here is code
https://codesandbox.io/s/stoic-benz-lxb8i
useEffect(() => {
console.log("====");
(async () => {
var a = await FETCH_API();
console.log("sssss");
setState(a);
console.log(a);
})();
}, []);
const getForm = () => {
try {
return state.map((i, index) => {
switch (i.type) {
case "text":
return (
<div key={index}>
<label>{i.label}</label>
<input type="text" ref={register()} name={i.name} />
</div>
);
case "select":
if (i.watch) {
watch(i.name, null);
}
return (
<div key={index}>
<label>{i.label}</label>
<select name={i.name} ref={register()}>
{i.options.map((i, idx) => {
return (
<option key={idx} value={i}>
{i}
</option>
);
})}
/
</select>
</div>
);
default:
return <div>ddd</div>;
}
});
return <div>ddd</div>;
} catch (e) {}
};
I don’t wan’t want to do any harcoading like
useEffect(()=>{
},[‘first’])
can we watch or add useeffect dynamically to watch change dynamically ?
Any update
this is a simpl two select. Where frist select depend on second
import React,{useState, useEffect} from "react"
const App = () => {
const [state,setState] = useState({
option1:["one","two"],
option2: []
})
useEffect(()=> {
(async () => {
var a = await FETCH_API();
console.log("sssss");
setState({
...state,
option2: a
});
console.log(a);
})();
},[state.option1])
return(
<div>
<select>
{
state.option1.map((i,idx)=>{
return(
<option key={idx} value={i}>
{i}
</option>
)
})
}
</select>
<select>
{
state.option2.map((i,idx)=>{
return(
<option key={idx} value={i}>
{i}
</option>
)
})
}
</select>
</div>
)
}
export default App
I have a <select> where the <option> is dynamically generated. Furthermore there is an <input> and as the user types it matches the characters filtering down the number of <option>. So if the <input> is blank it returns the full list and there will be a message that says 116 Results Found. As they type "head" it will update to 8 Results Found. Pretty common thing.
I can't figure out how to get the count. I am trying to do something in the _.map that counts i++ and then this.setState({ count: i }); however, this basically bricks the browser as it gets stuck in an infinite loop (well, it generates thousands of the same error before I force close it because the browser is locked while it is computing).
The other idea is to count the number of <option> elements, but I can't figure out how to implement that.
Any suggestions?
import _ from 'lodash';
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Link } from 'react-router-dom';
class BasicQuery extends Component {
constructor(props) {
super(props);
this.state = {
term: '',
count: ''
};
}
onInputChange(term) {
this.setState({ term });
}
renderOptionsSelect(term) {
var filterTerm = this.state.term.toLowerCase();
var i = 0;
return _.map(this.props.pos_list, p => {
if (p.pos_code.toLowerCase().match(filterTerm)) {
i++;
return (<option key={p.pos_code} value={p.pos_code}>{p.pos_code}</option>);
}
this.setState({ count: i });
});
}
// render the main element of the container
render() {
return (
<div className='panel panel-default'>
<div className='panel-heading'>
<h4><strong>Basic Query</strong></h4>
</div>
<div className='panel-body'>
<input
className='form-control'
placeholder='Enter Keyword or Position Code'
value={this.state.term}
onChange={event => this.onInputChange(event.target.value)}
/>
<hr />
<h4>Position:</h4>
<select className='form-control'>
<option></option>
{this.renderOptionsSelect()}
</select>
<br />
<h4>Data Cut:</h4>
<select className='form-control' disabled={true} />
</div>
</div>
);
}
}
// map the state in the Redux store to props
function mapStateToProps(state) {
return {
results: state.results.results,
pos_list: state.results.pos_list
}
}
export default connect (mapStateToProps, null)(BasicQuery);
I would do:
onInputChange(term) {
const filterTerm = term.toLowerCase();
const filteredItems = this.props.pos_list.filter( p =>
p.pos_code.toLowerCase().match(filterTerm)
)
this.setState({
term,
filteredItems,
count: filteredItems.length
});
}
Then in render:
this.state.filteredItems.map( p =>
<option key={p.pos_code} value={p.pos_code}>{p.pos_code}</option>
)
In your render method you can first call renderOptionsSelect and then get the number of generated options (since it is an array) and display it. And then I would also filter your array of options like this:
renderOptionsSelect(term) {
var filterTerm = this.state.term.toLowerCase();
return this.props.pos_list
.filter(p => p.pos_code.toLowerCase().match(filterTerm))
.map(p => (<option key={p.pos_code} value={p.pos_code}>{p.pos_code}</option>));
}
render() {
const options = this.renderOptionsSelect();
const numberOfResults = options.length;
return (
<div className='panel panel-default'>
<div className='panel-heading'>
<h4><strong>Basic Query</strong></h4>
<p>{numberOfResults} Results Found</p>
</div>
<div className='panel-body'>
<input
className='form-control'
placeholder='Enter Keyword or Position Code'
value={this.state.term}
onChange={event => this.onInputChange(event.target.value)}
/>
<hr />
<h4>Position:</h4>
<select className='form-control'>
<option></option>
{options}
</select>
</div>
</div>
);
}
The infinite loop is because you call setState in render renderOptionsSelect method, which is called in render method.
You can't call setState in render method, it should raise a warning.
To solve your problem, you might calculate the number of item on the onInputChange method.
I making a react app and I have a parent component Search with child components Input and Result. Input has a drop down menu which passes a value, genreValue, to Search, through a callback function when a button is clicked. Search then makes an api call, which works fine.
My problem is it takes two clicks of the button for the new API data to render. Looking at other SO questions I suspect I need to pass genreValue as an argument to the cb function, or my onClick is only initialising, rather than invoking it on the first click.
It's a pretty simple app so I wouldn't think Flux etc would be needed. My console logs seem to show the value being changed in the Search and Input components.
So what am I doing wrong?
Search.js
let Search = React.createClass ({
getInitialState(){
return {
movies: ['Men In Black'],
genreValue: '12'
};
},
componentDidMount(){
this.getMovies()
},
getMovies(){
let genre = this.state.genreValue;
let url = `http://api.themoviedb.org/3/discover/movie?${key}&with_genres=${genre}`;
Request.get(url).then((response) => {
console.log('response.body.results', response.body.results)
this.setState({
movies: response.body.results.map(function(movie){
return movie.title
})
});
});
},
handleGenre(newGenre) {
this.setState({ genreValue: newGenre })
return this.getMovies();
},
render(){
console.log(this.state.movies)
console.log('genreValue state', this.state.genreValue)
return (
<div>
<Input genre={this.state.genreValue} onGenreChanged={this.handleGenre}/>
<ul>
{this.state.movies.map( function(movie){
return <Results key={movie.id} data={movie}/>;
})}
</ul>
</div>
);
}
});
export default Search;
Input.js
let Input = React.createClass ({
selectHandler(){
return this.props.onGenreChanged(this.refs.genre.value);
},
render() {
console.log('genreValue prop', this.props.genre);
console.log('refs', this.refs.genre)
return <div>
<select ref="genre">
<option value="28">Action</option>
<option value="12">Adventure</option>
<option value="16">Animation</option>
<option value="35">Comedy</option>
<option value="80">Crime</option>
<option value="99">Documentary</option>
<option value="18">Drama</option>
<option value="10751">Family</option>
<option value="14">Fantasy</option>
<option value="10769">Non-english</option>
<option value="36">History</option>
</select>
<button onClick={this.selectHandler} value="Go">Go</button>
</div>
}
});
export default Input;
In the handleGenre function, state may not have updated when this.getMovies is called. You could change it to the following:
handleGenre(newGenre) {
this.setState({ genreValue: newGenre }, function() {
return this.getMovies();
});
},
Or, probably better practice would be to call this.getMovies in a componentDidUpdate lifecycle function if genreValue has changed:
componentDidUpdate: function(prevProps, prevState) {
if (prevState.genreValue !== this.state.genreValue) {
this.getMovies();
}
}