I have a bunch of checkboxes with the following markup
<input type='checkbox' data-id='123' data-label='abc' ref={checkboxRef} />
<input type='checkbox' data-id='456' data-label='xyz' ref={checkboxRef} />
And a state which is initially set as an empty array
const [contacts, setContacts] = useState([])
What I want to do is update the state with an object of a checkbox's data based on whether it's checked or not. If checked, it's data is to be added to the state and if unchecked, remove it.
Expected state after a checkbox is checked
[
{ id: '123', label: 'abc' }
]
I've used a ref for now to the input and getting the data of it but can't figure out how to go about updating the state.
const handleToggle = () => {
setIsChecked(prevState => !isChecked)
const id = checkboxRef.current.getAttribute('data-id')
const label = checkboxRef.current.getAttribute('data-label')
}
I have solved it. Check it here.
https://codesandbox.io/s/affectionate-fermi-f6bct
Full code hereby is
import React, { useState } from "react";
import "./styles.css";
export default function App() {
const [contacts, setContacts] = useState([]);
const ref1 = React.createRef();
const ref2 = React.createRef();
const handleClick = (ref) => {
const id = ref.current.getAttribute("data-id");
const label = ref.current.getAttribute("data-label");
if (contacts.map((e) => e.id).includes(id)) {
setContacts(contacts.filter((e) => e.id !== id));
} else {
setContacts([...contacts, { id, label }]);
}
console.log(contacts);
};
return (
<div className="App">
<input
type="checkbox"
data-id="123"
data-label="abc"
ref={ref1}
onClick={() => {
console.log("hi");
handleClick(ref1);
}}
/>
<input
type="checkbox"
data-id="456"
data-label="xyz"
ref={ref2}
onClick={() => handleClick(ref2)}
/>
</div>
);
}
Related
I have created 3 conditional lists with react-select but the third element cannot render the selected value, I am trying to update it even with useEffect but I don't know if it is correct.
Example of my code:
import React, { useState, useEffect } from "react";
import Select from "react-select";
import data from "../data.json";
const Dropdowns = ()=>{
const [product, setProduct] = useState(null);
const [type, setType] = useState(null);
const [typesList, setTypesList] = useState(null);
const [title, setTitle] = useState(null);
const [titlesList, setTitlesList] = useState(null);
// handle change event of the product dropdown
const handleProductChange = (obj) => {
setProduct(obj);
setTypesList(obj.types)
setType(null);
};
// handle change event of the types dropdown
const handleTypesChange = (obj) => {
setType(obj);
setTitlesList(obj.titles);
};
// handle change event of the titles dropdown
const handleTitlesChange = (obj) => {
setTitle(obj);
};
useEffect( () => {
if ( title ) {
setTitle(title)
}
}, [title])
return (
<>
<br />
<h3>Products</h3>
<Select
placeholder="Select Product"
value={ product }
options={ data }
onChange={ handleProductChange }
getOptionLabel={ (x) => x.product }
/>
<br />
<h3>Types</h3>
<Select
placeholder="Select Types"
value={ type }
options={ typesList }
onChange={ handleTypesChange }
getOptionLabel={ (x) => x.type }
/>
<br />
<h3>Titles</h3>
<Select
placeholder="Select Title"
value={ title }
options={ titlesList }
onChange={ handleTitlesChange }
getOptionLabel={ (x) => x }
/>
</>
)
}
export default Dropdowns;
Thank you very much for your help!
I'm trying to set a form field value with useState.
The settings.values.apiKey variable has a value, but the textarea element is empty. What's wrong with my useState?
I tried to change value={apiKey} to value={settings.values.apiKey} and then the value is displayed, but then I can't change the value of the field. When I try to enter something, it always shows the original value.
App.js
const App = () => {
const [apiKey, setApiKey] = useState(settings.values.apiKey)
useEffect(() => {
const getSettings = async () => {
const settingsFromServer = await fetchSettings()
setSettings(settingsFromServer)
}
getSettings()
}, [])
const fetchSettings = async () => {
const res = await fetch('http://127.0.0.1/react-server/get.php')
return await res.json()
}
const saveSettings = async (settings) => {
}
return (
<div className="container">
<Header />
<Settings
settings={settings}
saveSettings={saveSettings}
/>
<Footer />
</div>
);
}
export default App;
Settings.js:
import { useState } from 'react';
const Settings = ({ settings, saveSettings }) => {
const [apiKey, setApiKey] = useState(settings.values.apiKey)
const onSubmit = (e) => {
e.preventDefault()
saveSettings({ apiKey})
}
return (
<div>
<form className='add-form' onSubmit={onSubmit}>
<div className='form-control'>
<label>Api key</label>
<textarea
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
/>
</div>
<input type='submit' value='Save settings' className='mt15' />
</form>
</div>
)
}
export default Settings
It looks like by mistake you have used apiKey in App.js file as your state variable. It should be replaced by settings.
const [settings, setSettings] = React.useState();
The above code would make value={apiKey} work properly for textarea in Settings.js file.
And, then onChange will also start working properly.
UPDATE
In addition to the above mentioned error, in case settings props is undefined in Settings.js, this might cause your code to break at useState. So, instead put a check for settings values in useEffect and then set the value. The code would look like this or you can check the codesandbox link here for working demo.
Settings.js
import { useEffect, useState } from "react";
const Settings = ({ settings, saveSettings }) => {
const [apiKey, setApiKey] = useState();
useEffect(() => {
if (settings?.values?.apiKey) {
setApiKey(settings.values.apiKey);
}
}, [settings]);
const onSubmit = (e) => {
e.preventDefault();
saveSettings({ apiKey });
};
return (
<div>
<form className="add-form" onSubmit={onSubmit}>
<div className="form-control">
<label>Api key</label>
<textarea
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
/>
</div>
<input type="submit" value="Save settings" className="mt15" />
</form>
</div>
);
};
export default Settings;
App.js
const [settings, setSettings] = useState()
const saveSettings = async (settings) => {
setSettings(settings);
}
i have 3 components: Form (parent), Picklist and ParagraphBox (children); based on the select of the picklist, i render ParagraphBox and also a "+" button. What i would like to achieve is on the click of the plus button, render another ParagraphBox, just under the first. I would also like the remove functionality.
My ParagraphBox component has a title and a content, and i want to give the adding a progressive number:
e.g Paragraph 1
Content: ....
Paragraph 2
Content: ....
And so on
Here's my ParagraphBox component:
import React, { useState, useEffect } from 'react';
export default function ParagraphBox(props) {
const [paragrafo, setParagrafo] = useState({})
useEffect(() => {
console.log('paragrafo ', paragrafo)
props.onChange(paragrafo)
}, [paragrafo])
const onChange = (e) => {
const titolo = e.target.name
const contenuto = e.target.value
setParagrafo({
...paragrafo,
[titolo]: contenuto
})
}
return (
<div className = "paragraph-box">
<label>
{props.labelInputBox}
<div>
<input type="text" name="titolo" value={paragrafo.titolo || ''} onChange={onChange}/>
</div>
{props.labelTextArea}
<div>
<textarea id="subject" name="contenuto" placeholder="Inserisci contenuto.." style={{height: "45x", width: "400px"}} value={paragrafo.contenuto || ''} onChange={onChange} />
</div>
</label>
</div>
)
}
Here is my Form component:
import React, { useState, useEffect, useRef } from 'react';
import './Form.css'
import createDocument from '../pdfTool';
import finalita from '../icons/finalita.PNG';
import Picklist from './Picklist.js';
import ParagraphBox from './ParagraphBox';
export default function Form() {
const [flagImg, setFlagImg] = useState(false)
const [flagPar, setFlagPar] = useState(false)
const [paragArray, setParagArray] = useState([
{titolo: '', contenuto: ''}
])
const handleChange = (e) => {
console.log('e ', e)
console.log('e.titolo PARENT ', e.titolo)
console.log('e.contenuto PARENT ', e.contenuto)
setParagArray({
...paragArray,
[e.titolo]: e.contenuto
})
}
useEffect(() => {
console.log('rendering useEffect')
console.log('flagPar: ', flagPar)
console.log('flagImg: ', flagImg)
console.log('paragArray ', paragArray)
}, [flagPar, flagImg, paragArray])
const handleSubmit = (evt) => {
evt.preventDefault(); //usato per evitrare il refresh del browser
}
const addParag = (parag) => {
console.log('paragArray PARENT ', paragArray)
}
const onSelect = (selectedValue) => {
console.log('valore selezionato nella picklist: ' + selectedValue)
if(selectedValue === 'Layout 2') {
setFlagImg(true)
setFlagPar(true)
}
}
return(
<div>
<Picklist onSelect={onSelect} label="Seleziona un layout di contratto: " pickVals={["Seleziona...", "Layout 1", "Layout 2", "Layout 3"]}/>
{flagImg ? (
<form onSubmit={handleSubmit}>
<Picklist onSelect={onSelect} label="Seleziona Immagine: " pickVals={["Seleziona...", "Immagine 1", "Immagine 2", "Immagine 3"]} />
</form>
) : null}
{flagPar ? (
<div>
<ParagraphBox labelInputBox="Paragfrafo 1" labelTextArea="Contenuto Paragrafo" onChange={handleChange}/>
<div id = "add-paragraph">
<button type="button" onClick={addParag}>+</button>
<input type="submit" value="Submit" />
</div>
</div>
) : null}
</div>
)
Thanks in advance for your time
I know this is old...but I just faced the same issue, so here it goes: JSX is just syntactic sugar for regular JavaScript. Therefore you can just create the component manually and make it available as part of your hook, i.e.:
custom hook:
import React, { useState } from 'react';
import Advise from '../../components/Advise/Advise';
const useAdvise = () => {
const [ showAdvise, setShowAdvise ] = useState(false)
const [ adviseMsg, setAdviseMsg ] = useState('')
const [ loading, setLoading ] = useState(false)
const hideAdvise = () => {
setShowAdvise(false)
}
const adviseComponent = React.createElement(Advise, {show:showAdvise, showSpinner:loading, hideAdvise:hideAdvise, children:adviseMsg})
return {
adviseComponent,
setShowAdvise,
setAdviseMsg,
setLoading
}
};
export default useAdvise;
component where I want it:
import useAdvise from '../hooks/useAdvise/useAdvise'
const Page = () => {
const {adviseComponent, setShowAdvise, setAdviseMsg, setLoading} = useAdvise()
return(
<div>
{adviseComponent}
</div>
)
}
hope it helps (cheers from Br as well)
I have 3 components. Main Component named 'XYZ' which contains 2 other components named 'RadioButton' and 'Textbox'. I need to update state of Textbox component (Setting value to 500) on change of RadioButton component. Below is my code. I am new to React development. Any help would be surely appreciated!
Main Component - XYZ.tsx
const XYZ = (props) => {
let lossCapacityValues = [
{ value: "Average", text: "Average" },
{ value: "Medium", text: "Medium" }
];
const lossCapacityChange = (event) => {
let ele = $(event.target);
};
return (
<div className="mel-box">
<RadioButton groupName="radio-loss-type" selectedValue="2" items={lossCapacityValues} onChange={lossCapacityChange} />
<TextBox value="1000" />
</div>
)
};
export default XYZ;
Child Component - 1- RadioButton.tsx
const RadioButton = (props) => {
const onChange = (event) => {
props.onChange && props.onChange(event);
}
return (
<div>
{
props.items.map((i: { "text": string, "value": string }, index: number) => {
return (
<div key={index} className="radiobutton-option-div">
<input
className="radio-custom"
type="radio"
name={props.groupName}
value={i.value}
defaultChecked={i.value === props.selectedValue}
onChange={onChange}
/>
<label className="radio-custom-label">
<span className="radio-custom-text">{i.text}</span>
</label>
</div>
)
})
}
</div>
);
};
export default RadioButton;
Child Component - 2- TextBox.tsx
const TextBox = (props) => {
const [state, setState] = React.useState({
value: props.value
});
const handleChange = (evt) => {
setState({ value: evt.target.value });
};
return (
<React.Fragment>
<div className="input-text">
<input
type="text"
value={state.value}
onChange={handleChange}
/>
</div>
</React.Fragment>
)
};
export default TextBox;
This can be done by lifting the state up, out of the TextBox component into the XZY component.
Working demo
In XYZ, put the text state handlers:
const XYZ = (props) => {
const [textState, setTextState] = React.useState({
value: 1000
});
const handleTextChange = (evt) => {
setTextState({ value: evt.target.value });
};
....
....
};
Pass them as props:
<TextBox value={textState.value} onChange={handleTextChange} />
Change your input to use them:
<input
type="text"
value={props.value}
onChange={props.onChange}
/>
In your XYZ you can check the selected radio value and set the text state accordingly.
const lossCapacityChange = (event) => {
//let ele = $(event.target);
if(event.target.value == 'Average'){
setTextState({ value: 500 });
}
};
Side note: you should avoid the use of jQuery in React unless there is a good reason for it. React uses a Virtual DOM which can be affected by jQuery use.
I want to grab the value of input inside the array when the button is clicked. How do i pass the input value to the function of button.
Any help would be appreciated. Thanks
import React, { useState, useEffect } from 'react'
export default function Todo(props) {
const [todo,settodo] = useState([]);
function getdata(){
//fetch data
settodo(data);
}
function SaveInput(id){
}
useEffect(() => {
getdata();
},[]);
return (
<React.Fragment>
<div>
{todo.map(function(item, key){
return <div>
<div>{item.name}</div>
<div>
<input type="text" name="inputval" onChange={() => handleChange(e)}>
<button onClick={()=> SaveInput(item.id)}></button>
</div>
</div>
})}
</div>
</React.Fragment>
)
}
You need to send item.id to your handleChange function,
<input type="text" name="inputval" onChange={(e) => handleChange(e,item.id)} />
You handleChange function should,
const handleChange = (e,id) => {
let val = e.target.value;
setInputVal(prevState =>({
...prevState,
[id]:val
}))
}
You must define a state to store input values,
const [inputVal,setInputVal] = useState({});
On the click of button you can access input state,
function SaveInput(id){
console.log(inputVal[id]);
}
Demo
You can save the inputs in a separate useState when the input is being changed, which can be later retrieved easily during the button click event.
Code below is an example and is not tested, but should give you some idea how to proceed.
import React, { useState, useEffect } from 'react'
export default function Todo(props) {
const [todo,settodo] = useState([]);
const [inputVal, setInputVal] = useState({});
function getdata(){
//fetch data
settodo(data);
}
function SaveInput(id){
let inputVal = inputVal[id];
// do other stuff.
}
useEffect(() => {
getdata();
},[]);
return (
<React.Fragment>
<div>
{todo.map(function(item, key){
return <div>
<div>{item.name}</div>
<div>
<input type="text" name="inputval" onChange={(e) => setInputVal({...inputVal, [item.id]: e.target.value })}>
<button onClick={()=> SaveInput(item.id)}></button>
</div>
</div>
})}
</div>
</React.Fragment>
)
}
One common pattern is to use the handleChange(event) function on input to set a state with the current value.
const [input,setInupt] = useState("");
function handleChange(event) {
setInput(event.target.value)
}
and when the button is clicked, you can use the value of the input state to pass on
<button onClick={()=> console.log(input))}>
First of all, If you are having an onChange method then you must have a value for that input as well or else it will display a warning for "uncontrolled input" and that input box is of no use to you unless you provide a value to it.
Secondly, you should use a state for the values of those input boxes and then you can access the values of input in the save button click function. Here is the example of how you can do it.
import React from 'react'
export default class Todo extends React.Component {
constructor(props) {
super(props);
this.state = {
inputIDs: {}
}
}
SaveInput = id => {
console.log("input value:", this.state[id]);
};
handleChange = (e, id) => {
this.setState({[id]: e.target.value});
};
render() {
const {inputIDs} = this.state;
const todo = [
{id: 1, val: "abc", name: "lorem"},
{id: 2, val: "xyz", name: "Ipsum"}
];
let todos = todo.map((item, key) => {
return <div key={key}>
<div>{item.name}</div>
<div>
<input type="text" value={this.state[item.id]} onChange={(e) => this.handleChange(e, item.id)}/>
<button onClick={() => this.SaveInput(item.id)}>Click Me!</button>
</div>
</div>
});
return (
<React.Fragment>
{todos}
</React.Fragment>
)
}
}