Reputation: 1195
I have been trying to get my dynamic form to work but I was wondering what the best practice is to save data generated like this.
My solution works but I have the feeling there is a better way of doing it.
At the moment I am saving both of the values of the input fields in a seperate array. But actually they belong together, so I have a link value and a content value that I need to save.
Later I need to map over these values to create new elements with the stored value.
The problem I have now is that I don't know how to map over two arrays at the same time. I am also wondering if it's not better to just create one Object with all of these values instead and then just map over that.
Hope someone can help me out.
This is my code:
class MediaInput extends React.Component {
render() {
const linkName = `link${this.props.index}`;
const contentName = `content${this.props.index}`;
return (
<div>
<ControlLabel>Media (optional)</ControlLabel>
<input
onChange={(event) => this.props.handleChangeUrl(event, this.props.index)}
name={ linkName }
value={ this.props.mediaUrls[this.props.index]}
className="form-control"
placeholder="Add your media url. We accept YouTube, Vimeo and SoundCloud links"
type="text"
/>
<input
name={ contentName }
onChange={(event) => this.props.handleChangeContent(event, this.props.index)}
value={ this.props.mediaContents[this.props.index]}
className="form-control"
placeholder="Add your media content"
type="text"
/>
</div>
);
}
}
export default class AddSparkShanghai extends Component {
constructor(props) {
super(props);
this.createSpark = this.createSpark.bind(this);
this.onChange = this.onChange.bind(this);
this.handleChangeUrl = this.handleChangeUrl.bind(this);
this.handleChangeContent = this.handleChangeContent.bind(this);
this.state ={
mediaFields: [],
content: [],
mediaUrls: [ null, null, null ],
mediaContents: [ {'', '', ''],
};
}
[...]
// Add/remove media fields
add() {
event.preventDefault();
const mediaFields = this.state.mediaFields.concat(MediaInput);
if (i < 3) {
this.setState({ mediaFields });
i++
} else {
Bert.alert('Only 3 media links are allowed', 'danger');
}
}
remove() {
event.preventDefault();
const lastElement = this.state.mediaFields.pop();
const mediaFields = this.state.mediaFields;
this.setState({ mediaFields });
i--
}
// Handle change media fields
handleChangeUrl(e, index) {
// Shallow copy of array
const mediaUrls = this.state.mediaUrls.slice();
let url = e.target.value
if (!/^https?:\/\//i.test(url)) {
url = 'http://' + url;
}
mediaUrls[index] = url;
this.setState({ mediaUrls});
}
handleChangeContent(e, index) {
// Shallow copy of array
const mediaContents = this.state.mediaContents.slice();
mediaContents[index] = e.target.value;
this.setState({ mediaContents });
}
[...]
const mediaFields = this.state.mediaFields.map((Element, index) => {
return <Element key={ index } index={ index } mediaUrls={this.state.mediaUrls} mediaContents={this.state.mediaContents} handleChangeUrl={this.handleChangeUrl} handleChangeContent={this.handleChangeContent} />
})
[...]
<div>
{ mediaFields }
<Button onClick={ () => this.add() }>Add media field</Button>
<Button onClick={ () => this.remove() }>Remove media field</Button>
</div>
Upvotes: 3
Views: 280
Reputation: 1195
With the help of JosephGarrone I was able to get this example working like I want!
Here is the full code:
class MediaInput extends React.Component {
render() {
const linkName = `link${this.props.index}`;
const contentName = `content${this.props.index}`;
return (
<div>
<ControlLabel>Media (optional)</ControlLabel>
<input
onChange={(event) => this.props.handleChangeUrl(event, this.props.index)}
name={ linkName }
value={ this.props.mediaData[this.props.index].link}
className="form-control"
placeholder="Add your media url. We accept YouTube, Vimeo and SoundCloud links"
type="text"
/>
<input
name={ contentName }
onChange={(event) => this.props.handleChangeContent(event, this.props.index)}
value={ this.props.mediaData[this.props.index].content}
className="form-control"
placeholder="Add your media content"
type="text"
/>
</div>
);
}
}
export default class AddSparkShanghai extends Component {
constructor(props) {
super(props);
this.handleChangeUrl = this.handleChangeUrl.bind(this);
this.handleChangeContent = this.handleChangeContent.bind(this);
this.state ={
mediaFields: [],
mediaData: [],
};
}
[...]
// Add/remove media fields
add() {
event.preventDefault();
const mediaFields = this.state.mediaFields.concat(MediaInput);
const mediaData = this.state.mediaData.slice();
mediaData.push({ link: "", content: "" });
if (i < 3) {
this.setState({ mediaData, mediaFields });
i++
} else {
Bert.alert('Only 3 media links are allowed', 'danger');
}
}
remove() {
event.preventDefault();
const lastElement = this.state.mediaFields.pop();
const mediaFields = this.state.mediaFields;
const mediaData = this.state.mediaData.slice();
mediaData.pop();
this.setState({ mediaData, mediaFields });
i--;
}
// Handle change media fields
handleChangeUrl(e, index) {
// Shallow copy of array
const tempData = this.state.mediaData.slice();
let url = e.target.value
if (!/^https?:\/\//i.test(url)) {
url = 'http://' + url;
}
tempData[index].link = url;
this.setState({ mediaData: tempData });
console.log(this.state.mediaData)
}
handleChangeContent(e, index) {
// Shallow copy of array
const tempData = this.state.mediaData.slice();
tempData[index].content = e.target.value;
this.setState({ mediaData: tempData });
console.log(this.state.mediaData)
}
[...]
const mediaFields = this.state.mediaFields.map((Element, index) => {
return <Element key={ index } index={ index } mediaData={this.state.mediaData} handleChangeUrl={this.handleChangeUrl} handleChangeContent={this.handleChangeContent} />
})
[...]
<div>
{ mediaFields }
<Button onClick={ () => this.add() }>Add media field</Button>
<Button onClick={ () => this.remove() }>Remove media field</Button>
</div>
Upvotes: 1
Reputation: 4161
Untested code, but it should work if you change the following:
Change this:
this.state ={
mediaFields: [],
content: [],
mediaUrls: [ null, null, null ],
mediaContents: [ {'', '', ''],
};
To:
this.state ={
mediaFields: [],
content: [],
data: [],
};
And change:
handleChangeUrl(e, index) {
// Shallow copy of array
const mediaUrls = this.state.mediaUrls.slice();
let url = e.target.value
if (!/^https?:\/\//i.test(url)) {
url = 'http://' + url;
}
mediaUrls[index] = url;
this.setState({ mediaUrls});
}
To:
handleChangeUrl(e, index) {
// Shallow copy of array
const tempData = this.state.data.slice();
let url = e.target.value
if (!/^https?:\/\//i.test(url)) {
url = 'http://' + url;
}
tempData[index].link = url;
this.setState({ tempData });
}
And change:
handleChangeContent(e, index) {
// Shallow copy of array
const mediaContents = this.state.mediaContents.slice();
mediaContents[index] = e.target.value;
this.setState({ mediaContents });
}
To:
handleChangeContent(e, index) {
// Shallow copy of array
const tempData = this.state.data.slice();
tempData[index].content = e.target.value;
this.setState({ tempData });
}
And lastly:
add() {
event.preventDefault();
const mediaFields = this.state.mediaFields.concat(MediaInput);
if (i < 3) {
this.setState({ mediaFields });
i++
} else {
Bert.alert('Only 3 media links are allowed', 'danger');
}
}
remove() {
event.preventDefault();
const lastElement = this.state.mediaFields.pop();
const mediaFields = this.state.mediaFields;
this.setState({ mediaFields });
i--;
}
To:
add() {
event.preventDefault();
const mediaFields = this.state.mediaFields.concat(MediaInput);
const data = this.state.data.slice();
data.push({ link: "", content: "" });
if (i < 3) {
this.setState({ data, mediaFields });
i++
} else {
Bert.alert('Only 3 media links are allowed', 'danger');
}
}
remove() {
event.preventDefault();
const lastElement = this.state.mediaFields.pop();
const mediaFields = this.state.mediaFields;
const data = this.state.data.slice();
data.pop();
this.setState({ data, mediaFields });
i--;
}
NOTE: Has been edited to fix the last lot of changes!
Upvotes: 1