I need to have this feature, when the cancel button is clicked, all the previous inputs shall be cleared. I mean, if the value in the textfield or scroll-bar should be reset to default. This is how I got now, the value still exists even when I click the cancel button (<button type="delete" onClick={deleteFilter} >X</button>) although the filter is disabled:
const [stateSelected, setStateSelected] = useState('')
const [jobType, setJobType] = useState('');
const [type, setType] = useState('');
var states = statepicker.getStates('us');
document.body.style = 'position:absolute;';
const stateChange = e => {
setStateSelected(e.target.value)
}
const jobChange = e => {
setJobType(e.target.value)
}
const typeChange = e => {
setType(e.target.value)
}
function filterOn(){
if(filter.position != "" || filter.state != "" || filter.type != ""){
return true;
} else{
return false;
}
}
function deleteFilter(){
setFilter({
position: "",
state: "",
type: ""
})
}
const handleSignUp = useCallback(async event => {
event.preventDefault();
setFilter({
position: jobType,
state: stateSelected,
type: type
});
setFilterOpen(false);
setJob([]);
})
return (
<div>
<div className="settings__Section">
<div className="settings__SectionHeader">
Filter
</div>
<form onSubmit={handleSignUp}>
<input className="settings__inputBox" name="occupation" type="occupation" placeholder="Job Title" onChange={jobChange}/>
<select className="settings__inputSelect" onChange={stateChange} value={stateSelected}>
<option value={""} disabled={true}>Select State</option>
{
states.map(state => (
<option value={state} name="state">{state}</option>
))
}
</select>
<select className="settings__inputSelect" onChange={typeChange} value={type}>
<option value={""} disabled={true}>Select Job Type</option>
<option value={"Entry Level"} name="type">Entry Level</option>
<option value={"Internship"} name="type">Internship</option>
<option value={"Fellowship"} name="type">Fellowship</option>
<option value={"Apprenticeship"} name="type">Apprenticeship</option>
</select>
<button type="submit" className="settings__signBtn">Save Filter</button>
</form>
{filterOn() &&
<div>
<p>Position: {filter.position} Location: {filter.state} Job Type: {filter.type}</p>
<button type="delete" onClick={deleteFilter} >X</button>
</div>
}
</div>
</div>
);
<form onSubmit={handleSignUp}>
<input className="settings__inputBox" name="occupation" type="occupation" placeholder="Job Title" onChange={jobChange}/>
<select className="settings__inputSelect" onChange={stateChange} value={stateSelected}>
<option value={""} disabled={true}>Select State</option>
{
states.map(state => (
<option value={state} name="state">{state}</option>
))
}
</select>
<button type="submit" className="settings__signBtn">Save Filter</button>
<button type="reset" >X</button>
</form>
Try with reset button it does the thing here I suppose
Ok, although there is some lines missing in your code i think i know what the problem is.
The filter renders if either position, state, or type is different from empty string.
But when you delete the filter you are only resetting position and state:
function deleteFilter(){
setFilter({
position: "",
state: ""
})
}
So to fix it, just add type to this state function being called:
function deleteFilter () {
setFilter({
position: '',
state: '',
type: ''
})
}
Related
This form works correctly as a regular form - submitting retains the dropdown values. When I use RHF's reset() to load data from the URL parameters, The input boxes show the default values but the select fields do not reflect the default values.
The functionality I'm going for here, is to load the values from the URL querystring params, which look like this:
http://localhost:3000/search?search=dev&location=&environment=On-Site&job_type=Internship&industry=&industry_category=&date_posted=&offset=0&count=25
...effectively making it "stateless", so we're able to pass this URL to anyone and it loads the form values, performs the search, etc.
import React, { useContext, useEffect, useState, useRef } from "react";
import { useForm, Controller } from "react-hook-form";
import InputMask from "react-input-mask";
import useLookupService from "../../service/lookup_service";
const Search = props => {
const { onSearch } = props;
const lookupSvc = useLookupService();
const [searchFormDefaults, setSearchFormDefaults] = useState(null);
const [locationOptions, setLocationOptions] = useState([]);
const [jobTypeOptions, setJobTypeOptions] = useState([]);
const [workEnvironmentOptions, setWorkEnvironmentOptions] = useState([]);
const [industryOptions, setIndustryOptions] = useState([]);
const [industryCategoryOptions, setIndustryCategoryOptions] = useState([]);
const {
register,
setValue,
handleSubmit,
control,
reset,
formState: { errors }
} = useForm({
defaultValues: searchFormDefaults,
context: JobSearchForm.type,
resolver: validator.validateResolver
});
useEffect(() => {
loadData();
}, []);
useEffect(() => {
if (!window.location.search) {
return;
}
const queryParams = new URLSearchParams(window.location.search);
let defaults = Object.fromEntries(queryParams);
setSearchFormDefaults(defaults);
reset(defaults);
}, [reset]);
const loadData = async () => {
let [
jobTypesData,
workEnvironmentsData,
industriesData,
industryCategoriesData
] = await Promise.all([
lookupSvc.getJobTypes(),
lookupSvc.getWorkEnvironments(),
lookupSvc.getIndustries(),
lookupSvc.getIndustryCategories()
]);
setJobTypeOptions([
<option key={-1} value="">Please select...</option>,
jobTypesData.map((x, i) => <option key={i} value={x.name}>{x.name}</option>)
]);
setWorkEnvironmentOptions([
<option key={-1} value="">Please select...</option>,
workEnvironmentsData.map((x, i) => <option key={i} value={x.name}>{x.name}</option>)
]);
setIndustryOptions([
<option key={-1} value="">Please select...</option>,
industriesData.map((x, i) => <option key={i} value={x.name}>{x.name}</option>)
]);
setIndustryCategoryOptions([
<option key={-1} value="">Please select...</option>,
industryCategoriesData.map((x, i) => <option key={i} value={x.name}>{x.name}</option>)
]);
};
return (
<div>
<div>
<div>
Filters
</div>
<div>
<a onClick={onClearClick}>
Clear Filters
</a>
</div>
</div>
<form onSubmit={handleSubmit(onSearch)} autoComplete="off">
<div>
<div>
<div>
<label>Search</label>
</div>
<input type="text" {...register("search")} />
{errors.search && <span className="validation">{errors.search}</span>}
</div>
<div>
<div>
<label>Location</label>
</div>
<input type="text" {...register("location")} onChange={onLocationChange} />
{errors.location && <span className="validation">{errors.location}</span>}
</div>
<div>
<div>
<label>Work Environment</label>
</div>
<select {...register("environment")}>
{workEnvironmentOptions}
</select>
</div>
<div>
<div>
<label>Date Posted</label>
</div>
<Controller
control={control}
name="date_posted"
render={({ field }) => (
<InputMask {...field} mask="99/99/9999" />
)}
/>
{errors.date_posted && <span className="validation">{errors.date_posted}</span>}
</div>
<div>
<div>
<label>Job Type</label>
</div>
<select {...register("job_type")}>
{jobTypeOptions}
</select>
</div>
<div>
<div>
<label>Job Industry</label>
</div>
<select {...register("industry")}>
{industryOptions}
</select>
</div>
<div>
<div>
<label>Industry Category</label>
</div>
<select {...register("industry_category")}>
{industryCategoryOptions}
</select>
</div>
<button type="submit">
Search
</button>
</div>
</form>
</div>
);
};
export default Search;
Note that the selected values and display values are set to the same - a string such as "On-Site".
While debugging, the first useEffect() is firing and loading the data before the second one, with reset as its param. I thought perhaps the lists were being reloaded and wiping out the selected value, but I don't see evidence of that.
When setting a breakpoint on the return line of the component, searchFormDefaults contains the expected values, yet they're always set to the default value on the screen (the initial "Please select..." when they're created.)
This is what I see for searchFormDefaults on the final render:
{
"search": "dev",
"location": "",
"environment": "On-Site",
"job_type": "Internship",
"industry": "",
"industry_category": "",
"date_posted": "",
"offset": "0",
"count": "25"
}
I'm fairly new to react-hook-form so I'm sure I've done something goofy, but I'm unable to spot it or find an explanation anywhere.
Figured it out. Was just an async issue that a simple flag solved.
const [isDataLoaded, setIsDataLoaded] = useState(false);
...then in my data loading function:
const loadData = async () => {
let [
jobTypesData,
workEnvironmentsData,
industriesData,
industryCategoriesData
] = await Promise.all([
lookupSvc.getJobTypes(),
lookupSvc.getWorkEnvironments(),
lookupSvc.getIndustries(),
lookupSvc.getIndustryCategories()
]);
setJobTypeOptions([
<option key={-1} value="">Please select...</option>,
jobTypesData.map((x, i) => <option key={i} value={x.name}>{x.name}</option>)
]);
setWorkEnvironmentOptions([
<option key={-1} value="">Please select...</option>,
workEnvironmentsData.map((x, i) => <option key={i} value={x.name}>{x.name}</option>)
]);
setIndustryOptions([
<option key={-1} value="">Please select...</option>,
industriesData.map((x, i) => <option key={i} value={x.name}>{x.name}</option>)
]);
setIndustryCategoryOptions([
<option key={-1} value="">Please select...</option>,
industryCategoriesData.map((x, i) => <option key={i} value={x.name}>{x.name}</option>)
]);
setIsDataLoaded(true);
};
...and the hook that needs it:
useEffect(() => {
if (!isDataLoaded) {
return;
}
if (!window.location.search) {
return;
}
const queryParams = new URLSearchParams(window.location.search);
let defaults = Object.fromEntries(queryParams);
setSearchFormDefaults(defaults);
reset(defaults);
}, [reset, isDataLoaded]);
I have a form with dynamic selectBoxes. Depending on the result of the first one, I call a function whose result will go into the second selectBox.
I found solutions:
Apparently onClick is not working on the option tag, so I put my function in the onChange of the select tag.
Or
I used useEffect to call the function when the variable changes.
These solutions above work, but my problem is that it works once, sometimes twice but not more. I have to reload the page and it doesn't work for all of that.
If anyone would like to dive into my code and tell me why it doesn't work every time.
Thank you.
the variables componentOriginName, componentTargetName and the functions getComponentOriginNames, getComponentTargetNames come from redux.
const [relationType, setRelationType] = useState({
type: props.type,
});
const [newRelation, setNewRelation] = useState({
componentOriginType: "",
componentOriginName: "",
componentTargetType: "",
componentTargetName: "",
data: "",
});
the Form :
<div className='container--info--tuple'>
<div className='group--input'>
<select
name='componentOriginType'
onChange={handleChange}
value={newRelation.componentOriginType}
required>
<option value=''></option>
{props.components &&
props.components.map((component, i) => {
return (
<option key={i} value={component.name}>
{component.name}
</option>
);
})}
</select>
<span className='floating--label--select required'>
Composant origine
</span>
</div>
<div className='group--input'>
<select
name='componentOriginName'
onChange={handleChange}
value={newRelation.componentOriginName}
required>
<option value=''></option>
{props.componentOriginName &&
props.componentOriginName.map((component, i) => {
return (
<option key={i} value={component.name}>
{component.name}
</option>
);
})}
</select>
<span className='floating--label--select required'>
Nom du composant d'origine
</span>
</div>
</div>
<div className='container--info--tuple'>
<div className='group--input'>
<select
name='type'
onChange={handleChangeType}
value={relationType.type}
required>
<option value=''></option>
{props.relations.map((relation, i) => {
return (
<option key={i} value={relation.name}>
{relation.name}
</option>
);
})}
</select>
<span className='floating--label--select required'>Relation</span>
</div>
</div>
<div className='container--info--tuple'>
<div className='group--input'>
<select
name='componentTargetType'
onChange={handleChange}
value={newRelation.componentTargetType}
required>
<option value=''></option>
{props.components &&
props.components.map((component, i) => {
return (
<option key={i} value={component.name}>
{component.name}
</option>
);
})}
</select>
<span className='floating--label--select required'>
Composant cible
</span>
</div>
<div className='group--input'>
<select
name='componentTargetName'
onChange={handleChange}
value={newRelation.componentTargetName}
required>
<option value=''></option>
{props.componentTargetName &&
props.componentTargetName.map((component, i) => {
return (
<option key={i} value={component.name}>
{component.name}
</option>
);
})}
</select>
<span className='floating--label--select required'>
Nom du composant cible
</span>
</div>
</div>
and the functions with my differents tests commented :
const handleChange = (event) => {
setNewRelation({
...newRelation,
[event.target.name]: event.target.value,
});
if (event.target.name === "componentOriginType") {
propsGetComponentOriginNames(event.target.value);
} else if (event.target.name === "componentTargetType") {
propsGetComponentTargetNames(event.target.value);
}
};
// =======================================================================
// const test = (type) => {
// props.getComponentOriginNames(type);
// };
// const handleChangeOrigin = (event) => {
// test(event.target.value);
// setNewRelation({
// ...newRelation,
// [event.target.name]: event.target.value,
// });
// };
// const handleChangeTarget = (event) => {
// props.getComponentTargetNames(event.target.value);
// setNewRelation({
// ...newRelation,
// [event.target.name]: event.target.value,
// });
// };
// =======================================================================
const propsGetComponentOriginNames = props.getComponentOriginNames;
const propsGetComponentTargetNames = props.getComponentTargetNames;
// useEffect(() => {
// propsGetComponentOriginNames(newRelation.componentOriginType);
// }, [
// propsGetComponentOriginNames,
// newRelation.componentOriginType,
// relationType.type,
// ]);
// useEffect(() => {
// propsGetComponentTargetNames(newRelation.componentTargetType);
// }, [
// newRelation.componentTargetType,
// propsGetComponentTargetNames,
// relationType.type,
// ]);
// =======================================================================
🚨 CODE DUPLICATION 🚨
So I am trying to have an inital state object like: {prop1: val1, prop2: val2} and get seperate values from two <selection> fields.
I am having problems with getting each event.target.value on submit.
The only solution I came up with is splitting the original state into different objects, duplicate the handler functions and merge them on submit. But as you can see this is getting ugly pretty quick, I am sure that this could be optimized but I am getting nowhere with the unique event handlers.
const [firstSelection, setFirstSelection] = useState({color: 'green'})
const [secondSelection, setSecondSelection] = useState({time: 'evening'})
const handleFirstChange = e => setFirstSelection({ color: e.target.value })
const handleSecondChange = e => setSecondSelection({ time: e.target.value })
const getSelection = e => {
e.preventDefault()
console.log({...firstSelection, ...secondSelection})
}
const Form = () => {
return (
<form>
<div className='selectWrapper'>
<select name='color' value={firstSelection.color} onChange={handleFirstChange}>
<option value='red'>Red</option>
<option value='blue'>Blue</option>
<option value='yellow'>Yellow</option>
<option value='green'>Green</option>
</select>
</div>
<div className='selectWrapper'>
<select name='time' value={secondSelection.time} onChange={handleSecondChange}>
<option value='morning'>Morning</option>
<option value='evening'>Evening</option>
<option value='night'>Night</option>
</select>
</div>
<input type='submit' value='Submit' onClick={getSelection}/>
</form>
)
}
Try this :
export default function Form() {
const [selection, setSelection] = useState({ color: "red", time: "morning" });
const handleChange = (e) => {
setSelection({ ...selection, [e.target.name]: e.target.value });
};
const getSelection = (e) => {
e.preventDefault();
console.log(selection);
};
return (
<form>
<div className="selectWrapper">
<select
name="color"
value={selection.color}
onChange={(e) => handleChange(e)}
>
<option value="red">Red</option>
<option value="blue">Blue</option>
<option value="yellow">Yellow</option>
<option value="green">Green</option>
</select>
</div>
<div className="selectWrapper">
<select
name="time"
value={selection.time}
onChange={(e) => handleChange(e)}
>
<option value="morning">Morning</option>
<option value="evening">Evening</option>
<option value="night">Night</option>
</select>
</div>
<input type="submit" value="Submit" onClick={getSelection} />
</form>
);
}
You can try to create just one fuction to update state that use the names of the <select>s tags as keys of the state object. And concentrate all the data into one state. I will call this single state as formState
const [formState, setFormState] = useState({color: 'green', time: 'evening'})
const handleInput = e => setFormState({
...formState,
[e.target.name]: e.target.value
})
const getSelection = e => {
e.preventDefault()
console.log(formState)
}
const Form = () => {
return (
<form>
<div className='selectWrapper'>
<select name='color' value={firstSelection.color} onChange={handleInput}>
<option value='red'>Red</option>
<option value='blue'>Blue</option>
<option value='yellow'>Yellow</option>
<option value='green'>Green</option>
</select>
</div>
<div className='selectWrapper'>
<select name='time' value={secondSelection.time} onChange={handleInput}>
<option value='morning'>Morning</option>
<option value='evening'>Evening</option>
<option value='night'>Night</option>
</select>
</div>
<input type='submit' value='Submit' onClick={getSelection}/>
</form>
)
}
You could create a reusable component for select and use object for selection value, retrieved by input name
const CustomSelect = ({ name, options, value, onChange }) => {
return (
<div className="selectWrapper">
<select name={name} value={value} onChange={onChange}>
{options.map((option) => (
<option value={option.value}>{option.label}</option>
))}
</select>
</div>
);
};
export default function Form() {
const selectData = [
{
name: "color",
options: [
{ label: "Red", value: "red" },
{ label: "Blue", value: "blue" },
{ label: "Yellow", value: "yellow" },
{ label: "Green", value: "green" }
]
},
{
name: "time",
options: [
{ label: "Morning", value: "morning" },
{ label: "Evening", value: "evening" },
{ label: "Night", value: "night" }
]
}
];
const [selection, setSelection] = useState({
color: "green",
time: "evening"
});
const getSelection = (e) => {
e.preventDefault();
console.log(selection);
};
return (
<div className="App">
<form>
{selectData.map((sd) => (
<CustomSelect
name={sd.name}
options={sd.options}
value={selection[sd.name]}
onChange={(event) =>
setSelection({
...selection,
[event.target.name]: event.target.value
})
}
/>
))}
<input type="submit" value="Submit" onClick={getSelection} />
</form>
</div>
);
}
Codesandbox demo
I am trying to update my MongoDB database containing data for a project information form through a PUT request on my action, but I am unable to get the handleSumbit function on my component to call my project action. I am receiving a 404 error. I have no clue what I am missing or have done wrong. Any insight would be appreciated.
My Component:
import React, { useState } from 'react'
import { connect } from 'react-redux'
import { Link } from 'react-router-dom'
import '../styles/ViewProject.css'
import { updateProjects } from '../actions/project.actions'
const EditProject = ({ project, match }) => {
const [name, setName] = useState('')
const [email, setEmail] = useState('')
const [projectName, setProjectName] = useState('')
const [projectType, setProjectType] = useState('')
const [multiple, setMultiple] = useState(false)
const [dueDate, setDueDate] = useState(Date)
const [url, setUrl] = useState('')
const [description, setDescription] = useState('')
const handleSubmit = e => {
e.preventDefault()
updateProjects(name, email, projectName, projectType, dueDate, url, description)
console.log('Data ', name, email, projectName)
}
return(
<form className="viewProjectContainer" onSubmit={handleSubmit}>
{project.map((task, i) => (
<div key={i++} >
{match.params.id === task._id ?
<div className="vp">
<div className="vpLeft">
<div className="vpTitle">
<input className='vpProjectName' defaultValue={task.projectName} onChange={e => setProjectName(e.target.value)} />
</div>
<div className="vpSub">
<p>Submitted by: </p>
<label>Name: </label>
<input className='vpName' defaultValue={task.name} onChange={e => setName(e.target.value)}/>
<label>Email: </label>
<input className='vpEmail' defaultValue={email} onChange={e => setEmail(e.target.value)}/>
</div>
<div className="vpDescription">
<label>{task.multiple === true ? "(Multiple)" : "Single"}</label>
<p>Project Type: </p>
<select className='formInput' onChange={e => setProjectType(e.target.value)} defaultValue={projectType}>
<optgroup label='Digital'>
<option value='banner'>Banner</option>
<option value='emailBlast'>Email Blast</option>
<option value='productImage'>Product Image</option>
<option value='socialMediaAd'>Social Media Ad</option>
<option value='video'>Video</option>
<option value='websiteMockup'>Website Mockup</option>
<option value='other'>Other (Please be specific in Description)</option>
</optgroup>
<optgroup label='Physical Product'>
<option value='bottleOpener'>Bottle Opener</option>
<option value='clothing'>Clothing</option>
<option value='keyChain'>Key Chain</option>
<option value='popSocket'>Pop Socket</option>
<option value='usb'>USB</option>
<option value='other'>Other (Please be specific in Description)</option>
</optgroup>
<optgroup label='Print'>
<option value='businessCard'>Business Card</option>
<option value='brochureBiFold'>Brochure - Bi-fold</option>
<option value='brochureTriSquare'>Brochure - Trifold Square</option>
<option value='brochureTriStandard'>Brochure - Trifold Standard</option>
<option value='catalogMore'>Catalog - Less than 50 pages</option>
<option value='catalogLess'>Catalog - More than 50 pages</option>
<option value='documentFull'>Document - Full Sheet</option>
<option value='documentHalf'>Document - Half Sheet</option>
<option value='flyer4x6'>Flyer - 4x6</option>
<option value='flyer5x7'>Flyer - 5x7</option>
<option value='flyer6x9'>Flyer - 6x9</option>
<option value='postCard4x6'>Post Card 4x6</option>
<option value='postCard5x7'>Post Card 5x7</option>
<option value='postCard6x9'>Post Card 6x9</option>
<option value='poster11x17'>Poster 11x17</option>
<option value='poster24x36'>Poster 24x36</option>
<option value='other'>Other (Please be specific in Description)</option>
</optgroup>
<optgroup label='Other'>
<option value='consultation'>Consultation</option>
<option value='labeling'>Labeling</option>
<option value='other'>Other (Please be specific in Description)</option>
</optgroup>
</select>
<div className="vpDescBox">
<textarea className='updateDescription' defaultValue={description} onChange={e => setDescription(e.target.value)}></textarea>
</div>
</div>
</div>
<div className="vpRight">
<div className="vpTicket">
<p>Ticket Number: {task.ticketNumber}</p>
<label>Submitted: {task.date} </label>
<label>Preferred due date: </label>
<input type='date' defaultValue={task.dueDate} onChange={e => setDueDate(e.target.value)}/>
</div>
<div>
<label>Reference links: </label>
<input type='text' defaultValue={task.url} onChange={e => setUrl(e.target.value)}/>
<label></label>
</div>
<div>
<p>Project Status:</p>
<p>Task Recieved: {task.recieved === true ? "Recieved" : "Not Revieved"}</p>
<p>Task in progress: {task.inProgress === true ? "In Progress" : "Not in Progress"}</p>
<p>Completed: {task.completed === true ? "Complete" : "Incomplete"}</p>
</div>
</div>
</div>: null}
</div>
))}
<div className='buttonsContainer'>
<button type='submit' className='projectViewButton1'><Link to={'/' + match.params.id}>Save</Link></button>
</div>
</form>
)
}
const mapStateToProps = state => ({
project: state.projects.project,
id: state.projects.id
})
export default connect(
mapStateToProps,
{ updateProjects }
)(EditProject)
My action:
export function updateProjects(name, email, projectName, projectType, dueDate, url, description) {
console.log('reducer ', name)
axios.put('api/projects/update/:id', {
name: name,
email: email,
projectName: projectName,
projectType: projectType,
// multiple: multiple,
dueDate: dueDate,
url: url,
description: description
}).then(resp => {
console.log("Data updated: ", resp.data)
})
}
My Backend:
// #route PUT api/projects/update/:id
// #desc Update projects
// #access Private
router.put('/update/:id', async (req, res) => {
try {
const {
name,
email,
projectName,
projectType,
multiple,
dueDate,
reference,
attachment,
description,
ticketNumber,
received,
inProgress,
completed
} = req.body
const project = await Project.findByIdAndUpdate(req.params.id, {
name,
email,
projectName,
projectType,
multiple,
dueDate,
reference,
attachment,
description,
ticketNumber,
received,
inProgress,
completed
})
res.json(project);
} catch (err) {
console.error(err.message)
res.status(500).send('Server Error')
}
})
Thank you for helping!
The endpoint you are hitting with the axios.put is api/projects/update/:id, which is incomplete. You should put your host as well. For example: http://localhost:8000/api/projects/update/:id
And also, you need to specify a proper value for :id in the URL. Not sure if you left that out in purpose for example's sake but just wanna point that one out too haha.
in your react project folder's package.json file :
"proxy":"http://localhost:8000"
your http url should be below :
axios.put('/api/projects/update/', {
params: {
id: 'value of id'
}
});
or :
axios.put(`/api/projects/update/${value of id}`)
I have been trying to get my dynamic form to work in my Meteor React application.
It was all working as requested before I started add this but now I can't get it to work. I'm getting a "Uncaught TypeError: Cannot set property '0' of undefined" error. this points to this line:
{this.state.inputs.map((input, idx) => <input
This my whole code, I know it's a bit messy so any feedback is highly appreciated:
import React, { Component } from 'react';
import { Row, Col, Checkbox, Radio, ControlLabel, HelpBlock, FormGroup, FormControl, Button, Tabs, Tab } from 'react-bootstrap';
import { Bert } from 'meteor/themeteorchef:bert';
import { insertComment } from '../../../api/comments/methods';
import ReactQuill from 'react-quill';
var s3Url = null;
export default class AddSparkShanghai extends Component {
constructor(props) {
super(props);
this.createSpark = this.createSpark.bind(this);
this.onChange = this.onChange.bind(this);
this.state ={
inputs: ['input-0'],
city: '',
person: '',
location: '',
title: '',
content: [],
mediaUrls: [],
};
}
componentWillMount(){
// we create this rule both on client and server
Slingshot.fileRestrictions("myFileUploads", {
allowedFileTypes: ["image/png", "image/jpeg", "image/gif"],
maxSize: 10 * 1024 * 1024 // 10 MB (use null for unlimited)
});
}
upload(file){
var uploader = new Slingshot.Upload("myFileUploads");
uploader.send(document.getElementById('input').files[0], function (error, downloadUrl) {
if (error) {
// Log service detailed response
alert (error);
}
else {
s3Url = encodeURI(downloadUrl);
Bert.alert('File uploaded!', 'success');
Meteor.users.update(Meteor.userId(), {$push: {"profile.files": downloadUrl}});
}
});
}
createSpark(event) {
event.preventDefault();
var formData = $('#form').serializeArray()
console.log(formData);
var mediaArray = [];
if (this.mediaUrls.value == 0) {
mediaArray = [];
} else {
mediaArray.push(encodeURI(this.mediaUrls.value));
console.log(this.mediaUrl.value);
console.log(mediaArray);
}
const city = 'Shanghai';
const person = this.person.value;
const location = this.location.value;
const title = this.title.value;
const content = this.state.content;
const fileLink = s3Url;
const timestamp = parseInt(this.props.timestamp);
const mediaUrls = mediaArray;
const approved = true;
const adminSpark = true;
const createdBy = Meteor.userId();
insertComment.call({
city, person, location, title, content, fileLink, timestamp, approved, adminSpark, createdBy, mediaUrl,
}, (error) => {
if (error) {
Bert.alert(error.reason, 'danger');
} else {
Bert.alert('Spark added!', 'success');
}
});
}
onChange(html) {
this.setState ({ content: html });
}
appendInput() {
var newInput = `input-${this.state.inputs.length}`;
console.log (newInput);
this.setState({ inputs: this.state.inputs.concat([newInput]) });
}
render() {
const events = {
'text-change': delta => {
}
}
return (
<div className="background-container">
<form ref={(input) => this.sparkForm = input} onSubmit={(e) => this.createSpark(e)}>
<ControlLabel>Select your person (optional)</ControlLabel>
<select id="formControlsPerson" placeholder="Choose your person" className="form-control" ref={(input) => this.person = input}>
<option value='select'>Select your person</option>
<option value='jane'>Jane Siesta</option>
<option value='ben'>Ben Huang</option>
<option value='han'>Han Han</option>
<option value='mau'>Mau Mau</option>
<option value='void'>VOID</option>
<option value='tommy'>Tommy Hendriks</option>
<option value='gareth'>Gareth Williams</option>
<option value='gigi'>Gigi Lee</option>
</select>
<ControlLabel>Select your location (optional)</ControlLabel>
<select id="formControlsLocation" placeholder="Choose your location" className="form-control" ref={(input) => this.location = input}>
<option value='select'>Select your location</option>
<option value='shelter'>Shelter</option>
<option value='mansion'>The Mansion</option>
</select>
<ControlLabel>Title</ControlLabel>
<input type="text" label="Title" placeholder="Enter your title" className="form-control" ref={(input) => this.title = input}/>
<ControlLabel>Add Image</ControlLabel>
<div className="upload-area">
<p className="alert alert-success text-center">
<span>Click or Drag an Image Here to Upload</span>
<input type="file" id="input" className="file_bag" onChange={this.upload} />
</p>
</div>
<ControlLabel>Content</ControlLabel>
<div className='_quill'>
<ReactQuill
toolbar={false}
theme="snow"
ref='editor'
onChange={this.onChange}
events={events} />
</div>
<br />
<ControlLabel>Media (optional)</ControlLabel>
<div id="dynamicInput">
{this.state.inputs.map((input, idx) => <input
key={ input }
type="text"
label="Media"
placeholder="Add your media url"
className="form-control"
ref={(input) => this.mediaUrls[idx] = input}/> )}
</div>
<Button onClick={ () => this.appendInput() }>
Add media field
</Button>
<ControlLabel>Media (optional)</ControlLabel>
<div id="dynamicInput">
{this.state.inputs.map(input => <input key={input} type="text" label="Media" placeholder="Add your media url" className="form-control" ref={(input) => this.mediaUrl = input}/> )}
</div>
<Button onClick={ () => this.appendInput() }>
Add media field
</Button>
<Button type="submit" data-dismiss="modal">Submit</Button>
</form>
</div>
)}
}
I guess the problem is this line: ref={(input) => this.mediaUrls[idx] = input}/> )}, it seems like the value of this.mediaUrls is undefined