how to fix hook to set state in React JS? - javascript

I am trying to have a user set a function variable with an input number. I have a form they enter a number into, which needs to set the col const up top.
So far, i am using a simple hook, and col is being set (i console logged it), but its not producing the desired array. Im thinking its something getting messed up in the toggling at the bottom of the code.
function DataFrame(){
const [toggle, setToggle] = React.useState(false);
const [col, setCol] = useState(0)
var element = <li class="element"/>
var row = 3
var arr = []
var i
for (i = 0; i<row; i++){
arr.push(element)
}
const Element = () => <li className="element" />;
console.log(col)
return (
<div>
<div >
<div style={{fontFamily:'PressStart2P',opacity:'45%', width:'360px',
position:'absolute',left:'36px', top: '160px',color:'rgb(143, 0, 145)'}}>
</div>
<div >
<h1 class="dfHeader" style={{left:'30px'}}>
DataFrames :<br></br></h1>
<h1 class='dfHeader2'style={{top:'150px',left:'30px'}}>
constructor</h1>
<div class="codeBorder" style={{scrollbarColor:'#6a00ff'}}>
<div class="textbox" style={{width:'180px'}}>
<p class="instructions"></p>
<p class="instructions2"></p>
<p class="instructions3">
<form class="codeForm">
<label>
enter dimensions:
<input type="number" name="dimension" onKeyUp=
{e => setCol(e.target.value)} />
</label>
<input class='goButton' type="submit" value="run" />
</form>
<br/><br/></p>
<p class="instructions3">
<form class="codeForm">
<label>
input code:
<input type="number" name="dimension" />
</label>
<input class='goButton' type="submit" value="run" />
</form></p>
<p class="instructions3">
<form class="codeForm">
<label>
input code:
<input type="number" name="dimension" />
</label>
<input class='goButton' type="submit" value="run" />
</form></p>
<p class="instructions3">
<form class="codeForm">
<label>
input code:
<input type="number" name="dimension" />
</label>
<input class='goButton' type="submit" value="run" />
</form> </p>
</div>
</div>
<div class="btnConsole">
<button class="dfButton" onClick={()=>setToggle( (prev) => (!prev) )}>
</button>
</div>
</div>
<div class="monitor"style={{}}>
<div class="superScreen">
<div class="screenDiv" >
<div id="subScreen" class="subScreen">
{[...Array(col).keys()].map(ul => (
<ul key={ul}>
{toggle &&
[...Array(row).keys()].map(
li => <Element key={li} />)}
</ul>
))}
</div>
</div>
</div>
</div>
<br/>
</div>
</div>
)
}
export default DataFrame;
ReactDOM.render(<DataFrame />, document.getElementById('root'));
Any help is appreciated as always!

onKeyUp={e => setCol(e.target.value)}
this is the cause of your problem. e.target.value is a string, you are setting col equal to a string. Consequently, [...Array(col).keys()] gives you an array of length 1.
const col = '5';
console.log([...Array(col).keys()]);
Change
onKeyUp={e => setCol(e.target.value)}
to
onKeyUp={e => setCol(Number(e.target.value))}

Related

Adding elements dynamically

I'm trying to add elements dynamically through javascript but whenever I try opening up the page they appear for a split second then disappear
I take a number of process from the input tag and run a loop to create each element individually
I tried removing everything from the event and only call a function which I placed the code in but didn't work
const numberOfProcesses = document.getElementById("numberOfProcesses").value;
const timeQuantum = document.getElementById("timeQuantum").value;
const start = document.getElementById("start");
const processDiv = document.getElementById("processDiv");
const burstDiv = document.getElementById("burstDiv");
start.addEventListener("click", (event) => {
for (let i = 0; i < numberOfProcesses; i++) {
let pLabel = document.createElement("label");
pLabel.setAttribute("id", `process ${i}`);
pLabel.innerText = `Process ${i}`;
let pInput = document.createElement("input");
pInput.setAttribute("type", "number");
pInput.setAttribute("id", `process ${i}`);
let bLabel = document.createElement("label");
bLabel.setAttribute("id", `burstTime ${i}`);
bLabel.innerText = `Burst Time ${i}`;
let bInput = document.createElement("input");
bInput.setAttribute("type", "number");
bInput.setAttribute("id", `burstTime ${i}`);
processDiv.appendChild(pLabel);
processDiv.appendChild(pInput);
burstDiv.appendChild(bLabel);
burstDiv.appendChild(bInput);
console.log(pLabel, pInput, bLabel, bInput);
}
});
<form action="">
<div>
<label for="numberOfProcesses">Enter Number Of Processes</label>
<input type="number" name="Number Of Processes" id="numberOfProcesses" value="5" />
</div>
<br />
<div>
<label for="timeQuantum">Enter Time Quantum</label>
<input type="number" name="time quantum" value="5" id="timeQuantum" />
</div>
<button id="start">Start</button>
</form>
</section>
<br /><br />
<section>
<form action="">
<div id="processDiv">
<label for="process0">P0</label>
<input type="number" name="process" id="process0" />
</div>
<div id="burstDiv">
<label for="burstTime0">Burst Time</label>
<input type="number" name="burst time" id="burstTime0" />
</div>
<button id="excute">Execute</button>
</form>
Remove action="" and set type attribute to button if nothing is submitted. The behaviour you describe is due to the form being submitted.
Do like this and you can see you console log for other errors:
<form>
<div>
<label for="numberOfProcesses">Enter Number Of Processes</label>
<input type="number" name="Number Of Processes" id="numberOfProcesses" value="5" />
</div>
<br />
<div>
<label for="timeQuantum">Enter Time Quantum</label>
<input type="number" name="time quantum" value="5" id="timeQuantum" />
</div>
<button type="button" id="start">Start</button>
</form>

How can I convert a state component that has a toggle function to a stateless one in reactjs?

I am trying to convert the following component to a functional one so I could use the react context API?
The JSX code for the component I want to convert
class pizzaModal extends Component {
state = {
selected: "small",
showModal: true,
selectedOrder: null
}
toggleHandler = (size)=> ()=>{
this.setState({
toggle: size
});
}
addToOrders = (p)=>{
this.setState(prevState=>({
selectedOrder: p
}))
}
render (){
let attachedClasses = [styles.ImageContainer]
if(this.state.toggle==='small'){
attachedClasses = [styles.ImageContainer, styles.Small]
}
if(this.state.toggle==="medium"){
attachedClasses = [styles.ImageContainer, styles.Medium]
}
if(this.state.toggle==="large"){
attachedClasses=[styles.ImageContainer, styles.Large]
}
return (
<Aux>
<div className={styles.Pizzamodal}>
<div className={styles.ModalContainer}>
<div className={attachedClasses.join(' ')}>
<img src={this.props.image} alt="pizzapicture"/>
</div>
<div className={styles.DetailsContainer}>
<div>
<div className={styles.TextDetails}>
<h1>{this.props.name}</h1>
<p>{this.props.ingredients}</p>
</div>
<div>
<div className={styles.Form}>
<form className={styles.switchButton}>
<input type="radio" name="pizza" id="small" value="small" onChange={this.toggleHandler("small")}
checked={this.state.toggle==="small"}/>
<label for="small">Small</label>
<input type="radio" name="pizza" id="medium" value="medium" onChange={this.toggleHandler("medium")}
checked={this.state.toggle==="medium"}/>
<label for="medium">Medium</label>
<input type="radio" name="pizza" id="large" value="large" onChange={this.toggleHandler("large")}
checked={this.state.toggle==="large"}/>
<label for="large">Large</label>
</form>
</div>
<div className={styles.orderButton}>
<button onClick={this.props.addToOrders}>Add to basket for ₦{this.props.price}</button>
</div>
</div>
</div>
</div>
<div className={styles.Navbar} onClick={this.props.clicked}>
<div></div>
<div></div>
</div>
</div>
</div>
</Aux>
)
}
}
export default pizzaModal;
This is what I have done
const PizzaModal= (props) => {
const [Selected, setSelected] = useState('small')
const toggleHandler = (size)=>{
setSelected({
toggle: Selected
});
}
/*const [orders, setOrders] = useContext([CartContext]);
const addToOrders=()=>{
const pizza = {name: this.props.name, ingredients: this.props.ingredients, image: this.props.image, price: this.props.price}
setOrders([...orders, pizza])
}*/
let attachedClasses = [styles.ImageContainer]
if(setSelected(Selected==='small')){
attachedClasses = [styles.ImageContainer, styles.Small]
}
if(setSelected(Selected==="medium")){
attachedClasses = [styles.ImageContainer, styles.Medium]
}
if(setSelected(Selected==="large")){
attachedClasses=[styles.ImageContainer, styles.Large]
}
return (
<Aux>
<div className={styles.Pizzamodal}>
<div className={styles.ModalContainer}>
<div className={attachedClasses.join(' ')}>
<img src={props.image} alt="pizzapicture"/>
</div>
<div className={styles.DetailsContainer}>
<div>
<div className={styles.TextDetails}>
<h1>{props.name}</h1>
<p>{props.ingredients}</p>
</div>
<div>
<div className={styles.Form}>
<form className={styles.switchButton}>
<input type="radio" name="pizza" id="small" value="small" onChange={toggleHandler("small")}
checked={setSelected(Selected==='small')}/>
<label for="small">Small</label>
<input type="radio" name="pizza" id="medium" value="medium" onChange={toggleHandler("medium")}
checked={setSelected(Selected==='medium')}/>
<label for="medium">Medium</label>
<input type="radio" name="pizza" id="large" value="large" onChange={toggleHandler("large")}
checked={setSelected(Selected==='large')}/>
<label for="large">Large</label>
</form>
</div>
<div className={styles.orderButton}>
<button >Add to basket for ₦{props.price}</button>
</div>
</div>
</div>
</div>
<div className={styles.Navbar} onClick={props.clicked}>
<div></div>
<div></div>
</div>
</div>
</div>
</Aux>
)
}
export default PizzaModal;
I have no error messages on my IDE or on the webpage but when I click on the button that toggles the modal on I get a blank white screen with no error messages.
couple of mistakes i spotted in your code see if this changes anything
You dont need a seperate toggleHandler function to change value of Selected that is what setSelected is there for.
You gave setSelected an object
setSelected({toggle: Selected});
instead u should give it the value of Selected
setSelected(Selected);
And in all the if statements
if(setSelected(Selected==='small'))
is wrong u just have to check
if(Selected==='small')
4.And finally in OnChange
onChange={toggleHandler("small")}
you can just call setSelected
onChange={() => setSelected("small")}
The useState hook returns the current value of the state variable and a function to change it. In your case with
const [selected, setSelected] = useState('small')
you would use selected anywhere you previously would have used this.state.selected and use setSelected() anywhere you previously would have used this.setState({ selected }). So the first issue in your code is the misuse of the setter function when reading the state value.
When you want to pass a function to a component, you have to make sure you're passing the function and not a call to the function. For example
onChange={toggleHandler("large")}
will immediately get the return value of toggleHandler("large") and try to call that when the value changes. Your definition of toggleHandler doesn't return anything, so when you change your radio buttons, you're actually just trying to call undefined as a function, which of course doesn't work.
Also, the onChange handler of an input passes a synthetic event. You should use that to extract which radio button was pressed.
Try something like this:
const PizzaModal = (props) => {
const [selected, setSelected] = useState('small')
const toggleHandler = (event) => {
setSelected(event.target.value);
}
let attachedClasses = [styles.ImageContainer]
if (selected === 'small')) {
attachedClasses = [styles.ImageContainer, styles.Small]
}
if (selected === "medium")) {
attachedClasses = [styles.ImageContainer, styles.Medium]
}
if (selected === "large")) {
attachedClasses=[styles.ImageContainer, styles.Large]
}
return (
<Aux>
<div className={styles.Pizzamodal}>
<div className={styles.ModalContainer}>
<div className={attachedClasses.join(' ')}>
<img src={props.image} alt="pizzapicture"/>
</div>
<div className={styles.DetailsContainer}>
<div>
<div className={styles.TextDetails}>
<h1>{props.name}</h1>
<p>{props.ingredients}</p>
</div>
<div>
<div className={styles.Form}>
<form className={styles.switchButton}>
<input type="radio" name="pizza" id="small" value="small" onChange={toggleHandler} checked={selected === 'small'}/>
<label for="small">Small</label>
<input type="radio" name="pizza" id="medium" value="medium" onChange={toggleHandler} checked={selected === 'medium'}/>
<label for="medium">Medium</label>
<input type="radio" name="pizza" id="large" value="large" onChange={toggleHandler} checked={selected === 'large'}/>
<label for="large">Large</label>
</form>
</div>
<div className={styles.orderButton}>
<button >Add to basket for ₦{props.price}</button>
</div>
</div>
</div>
</div>
<div className={styles.Navbar} onClick={props.clicked}>
<div></div>
<div></div>
</div>
</div>
</div>
</Aux>
)
}
export default PizzaModal;
For future reference, errors also come through in the browser's Javascript console (usually in the developer tools section). You can check there for errors. My guess is that some combination of trying to call undefined as a function and a render loop due to the setSelected calls is what was causing your issue.

Why does localstorage multi-toggle state code work in jsfiddle but not on my webpage?

In last question I had assistance to create a code to save the state of a fairly complex toggle state. When a radio button is selected a checkbox slides down, when that checkbox is selected another one slides down. The reverse also occurs. Much of the code I do not understand. The problem now is that it works perfectly in jsfiddle.
https://jsfiddle.net/tomik23/ovysmutL/7/
However, it does not function on my webpage. When localstorage restores the state back to the webpage after a page refresh it automatically 'unchecks' the checkboxes about 2 seconds after load when they should have remained checked.
I totally stripped down my page to try to isolate this issue...the problem still exists. I read and tried all the similar stackoverflow problems eg( modified forms, added doc ready function,etc)... but obviously none of them helped.
The code works prior to localstorage insertion. When page is refreshed localstorage restores the state back to the webpage but it automatically 'unchecks' the checkboxes about 2 seconds after load when they should have remained checked. Does ANYBODY know what is going on AND HOW TO FIX THIS? I truly appreciate the help.
**HTML**
<form class="form" id="form-a" method="post" autocomplete="on">
<fieldset>
<div>
<p>
<label class="yes_text">Yes</label>
<span>
<input type="radio" data-show="next-a" id="dog" name="answer_name" value="yes">
</span>
</p>
<p>
<label>No</label>
<span>
<input type="radio" name="answer_name" value="no" checked>
</span>
</p>
</div>
</fieldset>
<fieldset id="next-a" class="hidden">
<div class="red">
<div>
<p>Include Red Dogs:</p>
</div>
<div>
<p>
<label class="yes_text_include">select to include</label>
<span>
<input type="checkbox" data-show="next-b" id="cat" class="red" name="red_name" value="">
</span>
</p>
</div>
</div>
<div id="next-b" class="hidden">
<div>
<p>Include Green Dogs:</p>
</div>
<div>
<p>
<label>select to include</label>
<span>
<input type="checkbox" name="green_name" class="cat" value="">
</span>
</p>
</div>
<div>
<p>
<label>select to include</label>
<span>
<input type="checkbox" name="blue_name" class="cat" value="">
</span>
</p>
</div>
</div>
</fieldset>
</form>
<form class="form" id="form-b" method="post" autocomplete="on">
<fieldset>
<div>
<p>
<label class="yes_text">Yes</label>
<span>
<input type="radio" data-show="next-a" id="dog" name="answer_name" value="yes">
</span>
</p>
<p>
<label>No</label>
<span>
<input type="radio" name="answer_name" value="no" checked>
</span>
</p>
</div>
</fieldset>
<fieldset id="next-a" class="hidden">
<div class="red">
<div>
<p>Include Red Dogs:</p>
</div>
<div>
<p>
<label class="yes_text_include">select to include</label>
<span>
<input type="checkbox" data-show="next-b" id="cat" class="red" name="red_name" value="">
</span>
</p>
</div>
</div>
<div id="next-b" class="hidden">
<div>
<p>Include Green Dogs:</p>
</div>
<div>
<p>
<label>select to include</label>
<span>
<input type="checkbox" name="green_name" class="cat" value="">
</span>
</p>
</div>
</div>
</fieldset>
</form>
**Javascript**
class CheckForm {
constructor(option) {
const forms = document.querySelectorAll(`${option}`);
forms.forEach(form => {
const formname = form.id;
this.formCheck(formname);
this.checkChecked(formname);
});
}
formCheck(formName) {
const form = document.querySelector(`#${formName}`);
form.addEventListener('click', e => {
const { target: { type, value, id, dataset: { show } } } = e;
switch (type) {
case 'radio': {
if (value === 'yes') {
$(`#${formName}`).find(`#${show}`).show(200);
this.saveToLocalstore(formName);
} else {
$(`#${formName} fieldset`).next().hide(200);
document.querySelector(`#${formName}`).reset();
localStorage.removeItem(formName);
this.removeAllChecked(formName);
}
break;
}
case 'checkbox': {
$(`#${formName}`).find(`#${show}`).toggle(200);
this.saveToLocalstore(formName);
if (id) {
this.removeAllChecked(formName, id);
}
break;
}
default:
break;
}
});
}
saveToLocalstore(formName) {
let allInput = document.querySelectorAll(`#${formName} input`);
let object = {};
allInput.forEach(item => {
object[item.name] = item.type === 'radio' ? true : item.checked;
});
localStorage.setItem(formName, JSON.stringify(object));
}
checkChecked(formName) {
const data = JSON.parse(localStorage.getItem(formName));
if (data) {
let i = 1;
for (let [key, value] of Object.entries(data)) {
const item = document.querySelector(`#${formName} input[name='${key}']`);
setTimeout(() => {
if (value) {
item.click();
}
}, i * 1000);
i++;
}
}
}
removeAllChecked(formName, id) {
if (id) {
let allInput = document.querySelectorAll(`#${formName} .${id}`);
allInput.forEach(item => {
item.checked = false;
});
} else {
const allHidden = document.querySelectorAll(`#${formName} .hidden`);
allHidden.forEach(item => {
item.removeAttribute('style', '');
});
}
}
}
new CheckForm('.form');
**CSS**
.hidden {
display: none;
}

HTML Form - similar input groups as an array of objects

So I have a form with two identical group of inputs that represent education info. There could be more than two as I want to include a button to create a new group so the user can put all his education background like in LinkedIn.
<form id="formCV" action="">
<div id="educationContainer">
<!-- First Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
<!-- Second Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
</div>
</form>
Now, if I use the FormData API to get the form data like this:
for(let entry of formData.entries()){
console.log(entry);
}
I get the following output:
(2) ["institutionName", "Harvard"]
(2) ["courseName", "Web Development"]
(2) ["startDate", "2000-11"]
(2) ["endDate", "2008-11"]
(2) ["institutionName", "Oxford"]
(2) ["courseName", "Business Management"]
(2) ["startDate", "2009-10"]
(2) ["endDate", "2010-05"]
What I want to achieve is to get the output in an organized way, like this:
education:[
{
institutionName:"Harvard",
courseName:"Web Development",
startDate:"2000-11",
endDate:"2008-11"
},
{
...
}
]
So I'm interested in knowing the best approach to achieve this. Thanks in advance for any help!
It does not make sense to have two equal forms, with one being sufficient.
In addition to the form you should have a list that shows each item added.
It's what I recommend.
Not sure whether this is the best approach, but you can achieve the desired structure like this:
const formCV = document.querySelector('#formCV');
const formData = new FormData(formCV);
function groupEducationData(inputGroupSize = 4) {
const result = [];
let educationObj = null;
let counter = 0;
for (const entry of formData.entries()) {
// Since the counter is divisible by the number of inputs in a group
// only if one form group finishes. And when one form group finishes,
// we need to add the object into the result array
if (counter % inputGroupSize === 0) {
// if this is the first iteration, the educationObj is null and
// we don't want to add it to the result array yet
// we only add the educationObj to the result array if it is
// an object containing the education info
if (educationObj) result.push(educationObj);
// initialize the educationObj at the start
// and after one form finishes
educationObj = {};
}
// add entry[0] as key to the object (e.g. 'institutionName')
// with the value of entry[1] (e.g. 'Harvard')
educationObj[entry[0]] = entry[1];
counter++;
}
return result.concat(educationObj);
}
console.log(groupEducationData());
<form id="formCV" action="">
<div id="educationContainer">
<!-- First Group -->
<div class="education">
<div>
<input type="text" name="institutionName" value="Harvard">
</div>
<div>
<input type="text" name="courseName" value="Web Development">
</div>
<div>
<input type="month" name="startDate" value="2000-11">
</div>
<div>
<input type="month" name="endDate" value="2008-11">
</div>
</div>
<!-- Second Group -->
<div class="education">
<div>
<input type="text" name="institutionName" value="Oxford">
</div>
<div>
<input type="text" name="courseName" value="Business Management">
</div>
<div>
<input type="month" name="startDate" value="2009-10">
</div>
<div>
<input type="month" name="endDate" value="2010-05">
</div>
</div>
</div>
</form>
You can try FormData.getAll() and iterate over each group entry.
const institutionNames = formData.getAll('institutionName');
const courseNames = formData.getAll('courseName');
...
const educations = [];
for (let i = 0; i < institutionNames.length; i++) {
educations.push({
institutionName: institutionNames[i],
courseName: courseNames[i],
...
});
}
This is also a way to populate your desired format data.
$(document).ready(function(){
$(":button").click(function(){
var educations=$("#formCV .education");
var data=[];
educations.each(function(i,education){
var set={}
$(education).find(":input").each(function(i,value){
set[$(value).attr("name")] = $(value).val();
});
data.push(set);
})
console.log("data",data)
});
});
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
</head>
<body>
<form id="formCV" action="">
<div id="educationContainer">
<!-- First Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
<!-- Second Group -->
<div class="education">
<div>
<input type="text" name="institutionName">
</div>
<div>
<input type="text" name="courseName">
</div>
<div>
<input type="month" name="startDate">
</div>
<div>
<input type="month" name="endDate">
</div>
</div>
</div>
<input type="button" value="click me"/>
</form>
</body>
</html>

Get Checked Radio Button's Value

I'm trying to have three radio buttons for three mutually exclusive choices in a form. The form also includes a text input and textarea. Upon clicking the submit button, the value of the checked radio button and that of the text input and textarea need to be assigned as values for props.
var Widget = React.createClass({
render: function() {
var widgetClasses = classNames('widget', this.props.widgetWidth);
return (
<div className={widgetClasses}>
<header className="widget-header">
<h3>{this.props.widgetTitle}</h3>
<p>{this.props.widgetDescription}</p>
</header>
<ul>
<li>Lorem ipsum</li>
</ul>
<ul>
<li>Dolor sit</li>
</ul>
</div>
)
}
});
var WidgetsContainer = React.createClass({
render: function() {
var widgetNodes = this.props.data.map(function(widget) {
return (
<Widget widgetTitle={widget.widgetTitle}
widgetWidth={widget.widgetWidth}
widgetDescription={widget.widgetDescription}>
</Widget>
);
});
return (
<div className="widgetsContainer">
{widgetNodes}
</div>
);
}
})
var Dashboard = React.createClass({
getInitialState: function() {
return {data: []}
},
handleWidgetConfig: function(widget) {
var widgets = this.state.data;
// var widget.id = Date.now();
var newWidgets = widgets.concat([widget]);
this.setState({data: newWidgets});
},
render: function() {
var displayedItems = this.state.data;
return (
<div className="dashboard-content">
<CreateWidgetDropdown updateWidgetConfig={this.handleWidgetConfig} />
<WidgetsContainer data={displayedItems} />
</div>
);
}
});
var CreateWidgetDropdown = React.createClass({
createNewWidget: function(e) {
e.preventDefault();
var widgetWidth = this.refs.widgetWidthInput.checked.value;
var widgetTitle = this.refs.widgetTitleInput.value;
var widgetDescription = this.refs.widgetDescriptionInput.value;
this.props.updateWidgetConfig({
widgetWidth: widgetWidth,
widgetTitle: widgetTitle,
widgetDescription: widgetDescription
});
},
render: function() {
return (
<div className="page-dropdown">
<div className="page-dropdown-header">
<h2 className="wrapper">Add a Widget</h2>
</div>
<div id="page-dropdown-content">
<form className="page-dropdown-form">
<div classNameName="choose-widget-type">
<h3>Choose a Widget Type</h3>
<div className="widget-type table">
<h4>Table</h4>
<div classNameName="widget-type-icon">
<img src="" alt="" />
</div>
<ul className="widgetWidth">
<li>
<label for="1/3 Width input">
1/3 Width
<input ref="widgetWidthInput" name="widgetWidth" type="checkbox" value="1/3 Width" />
</label>
</li>
<li>
<label for="2/3 Width input">
2/3 Width
<input ref="widgetWidthInput" name="widgetWidth" type="checkbox" value="2/3 Width" />
</label>
</li>
<li>
<label for="Full Width input">
Full Width
<input ref="widgetWidthInput" name="widgetWidth" type="checkbox" value="Full Width" />
</label>
</li>
</ul>
</div>
<div classNameName="create-widget-header">
<h3>Widget Header (Optional)</h3>
<label for="widget-title-input">
Widget Title (30 characters max)
<input type="text" ref="widgetTitleInput" required />
</label>
<label for="widget-description-input">
Widget Description (50 characters max)
<textarea ref="widgetDescriptionInput"></textarea>
</label>
<button onClick={this.createNewWidget}>Add Widget</button>
<button type="reset">Cancel</button>
</div>
</form>
</div>
</div>
)
}
});
ReactDOM.render(<Dashboard />, document.getElementById('dashboard-container'));
I would suggest you use an <input type="radio"> instead of <input type="checkbox">:
<ul className="widgetWidth">
<li>
<label>
1/3 Width
{' '}
<input name="width" type="radio" value="1/3 Width" defaultChecked/>
</label>
</li>
<li>
<label>
2/3 Width
{' '}
<input name="width" type="radio" value="2/3 Width" />
</label>
</li>
<li>
<label>
Full Width
{' '}
<input name="width" type="radio" value="Full Width" />
</label>
</li>
</ul>
Then if you use the form's onSubmit event...
<form className="page-dropdown-form" onSubmit={this.createNewWidget}>
...and give all your inputs name attributes, you can use the form's elements collection to get all the values you need with refs, and the radio input's value can be obtained using the .value getter on the collection it's represented by in form.elements:
createNewWidget(e) {
e.preventDefault()
var form = e.target
var width = form.elements.width.value
var title = form.elements.title.value
var description = form.elements.description.value
this.props.updateWidgetConfig({
width,
title,
description,
})
},
Live example: http://stackoverflow-38236634.surge.sh/
Gist you can clone to develop (with hot reloading) and build this example: https://gist.github.com/insin/45b7f66e01628601c0cc6b79767b0e4f
Full code for the working example:
var classNames = require('classnames')
var React = require('react')
var ReactDOM = require('react-dom')
var uuid = require('uuid')
var Widget = React.createClass({
render() {
var {widget} = this.props
var widgetClasses = classNames('widget', widget.width)
return <div className={widgetClasses}>
<header className="widget-header">
<h3>{widget.title}</h3>
<p>{widget.description}</p>
</header>
<ul>
<li>Lorem ipsum</li>
</ul>
<ul>
<li>Dolor sit</li>
</ul>
</div>
}
})
var WidgetsContainer = React.createClass({
render() {
return <div className="widgetsContainer">
{this.props.data.map(widget =>
<Widget key={widget.id} widget={widget}/>
)}
</div>
}
})
var Dashboard = React.createClass({
getInitialState() {
return {
data: []
}
},
handleWidgetConfig(widget) {
this.setState({
data: [
...this.state.data,
{id: uuid.v4(), ...widget},
]
})
},
render() {
return <div className="dashboard-content">
<CreateWidgetDropdown updateWidgetConfig={this.handleWidgetConfig} />
<WidgetsContainer data={this.state.data} />
</div>
}
})
var CreateWidgetDropdown = React.createClass({
createNewWidget(e) {
e.preventDefault()
var form = e.target
var width = form.elements.width.value
var title = form.elements.title.value
var description = form.elements.description.value
this.props.updateWidgetConfig({
width,
title,
description,
})
},
render() {
return <div className="page-dropdown">
<div className="page-dropdown-header">
<h2 className="wrapper">Add a Widget</h2>
</div>
<div id="page-dropdown-content">
<form className="page-dropdown-form" onSubmit={this.createNewWidget}>
<div className="choose-widget-type">
<h3>Choose a Widget Type</h3>
<div className="widget-type table">
<h4>Table</h4>
<div className="widget-type-icon">
<img src="" alt="" />
</div>
<ul className="widgetWidth">
<li>
<label>
1/3 Width
{' '}
<input name="width" type="radio" value="1/3 Width" defaultChecked/>
</label>
</li>
<li>
<label>
2/3 Width
{' '}
<input name="width" type="radio" value="2/3 Width" />
</label>
</li>
<li>
<label>
Full Width
{' '}
<input name="width" type="radio" value="Full Width" />
</label>
</li>
</ul>
</div>
<div className="create-widget-header">
<h3>Widget Header (Optional)</h3>
<label>
Widget Title (30 characters max)
{' '}
<input type="text" name="title" required />
</label>
<label>
Widget Description (50 characters max)
{' '}
<textarea name="description"></textarea>
</label>
</div>
<button type="submit">Add Widget</button>
<button type="reset">Cancel</button>
</div>
</form>
</div>
</div>
}
})
ReactDOM.render(<Dashboard />, document.getElementById('app'))

Categories

Resources