React multi-select-like component with filter
up vote
1
down vote
favorite
I am new to react (and javascript in general) but am enjoying learning it.
I have decided to make a little multiselect component which should work as follows:
I start with my data in one list, when I select an item from the list it goes into a second list (I can 'unselect' as well).
I also can filter the first list with a text input.
Before you review, I realize that there are likely existing solutions for this functionality that I could use, but I am just doing this to learn.
codepen here https://codepen.io/str8wavedave/pen/ZqWXQp
class MultiSelect extends React.Component {
render(){
return(
<ul>
{this.props.objects.map((object, index) => <li onClick = {() => this.props.onClickFunction(index)}>{object.name}</li>)}
</ul>
);
}
}
class DoubleMultiselect extends React.Component {
state = {
value: '',
filteredUnselectedObjects: this.props.objects,
unselectedObjects: this.props.objects,
selectedObjects: ,
}
filterList = (event) => {
var updatedList = this.state.unselectedObjects;
updatedList = updatedList.filter(function(item){
return item.name.toLowerCase().search(
event.target.value.toLowerCase()) !== -1;
});
this.setState({
filteredUnselectedObjects: updatedList,
value: event.target.value
})
}
handleSelect = (index) => {
var obj = this.state.filteredUnselectedObjects[index]
var newSelected = this.state.selectedObjects.slice()
newSelected.push(obj)
var newUnselected = this.state.unselectedObjects.slice()
var originalIndex = newUnselected.indexOf(obj);
newUnselected.splice(originalIndex, 1)
var newFilteredUnselected = this.state.filteredUnselectedObjects.slice()
newFilteredUnselected.splice(index, 1)
this.setState({
selectedObjects: newSelected,
unselectedObjects: newUnselected,
filteredUnselectedObjects: newFilteredUnselected
});
}
handleUnselect = (index) => {
var obj = this.state.selectedObjects[index]
var newUnselected = this.state.unselectedObjects.slice()
newUnselected.push(obj)
var newFilteredUnselected = this.state.filteredUnselectedObjects.slice()
if(obj.name.toLowerCase().search(this.state.value.toLowerCase()) !== -1)
{
newFilteredUnselected.push(obj)
}
var newSelected = this.state.selectedObjects.slice()
newSelected.splice(index, 1)
this.setState({
unselectedObjects: newUnselected,
selectedObjects: newSelected,
filteredUnselectedObjects: newFilteredUnselected
});
}
render() {
return(
<div>
<input type="text" onChange={this.filterList}/>
<MultiSelect objects={this.state.filteredUnselectedObjects} onClickFunction={this.handleSelect}/>
<MultiSelect objects={this.state.selectedObjects} onClickFunction={this.handleUnselect}/>
</div>
);
}
}
const PLAYERS =[
{name: 'Charlie', id: 1},
{name: 'David', id: 2},
{name: 'Eric', id: 3},
{name: 'Emily', id: 4},
{name: 'Peter', id: 5},
{name: 'Sam', id: 6},
{name: 'Doug', id: 7}
]
React.render(<DoubleMultiselect objects={PLAYERS}/>, document.getElementById('app'));
beginner form react.js jsx
add a comment |
up vote
1
down vote
favorite
I am new to react (and javascript in general) but am enjoying learning it.
I have decided to make a little multiselect component which should work as follows:
I start with my data in one list, when I select an item from the list it goes into a second list (I can 'unselect' as well).
I also can filter the first list with a text input.
Before you review, I realize that there are likely existing solutions for this functionality that I could use, but I am just doing this to learn.
codepen here https://codepen.io/str8wavedave/pen/ZqWXQp
class MultiSelect extends React.Component {
render(){
return(
<ul>
{this.props.objects.map((object, index) => <li onClick = {() => this.props.onClickFunction(index)}>{object.name}</li>)}
</ul>
);
}
}
class DoubleMultiselect extends React.Component {
state = {
value: '',
filteredUnselectedObjects: this.props.objects,
unselectedObjects: this.props.objects,
selectedObjects: ,
}
filterList = (event) => {
var updatedList = this.state.unselectedObjects;
updatedList = updatedList.filter(function(item){
return item.name.toLowerCase().search(
event.target.value.toLowerCase()) !== -1;
});
this.setState({
filteredUnselectedObjects: updatedList,
value: event.target.value
})
}
handleSelect = (index) => {
var obj = this.state.filteredUnselectedObjects[index]
var newSelected = this.state.selectedObjects.slice()
newSelected.push(obj)
var newUnselected = this.state.unselectedObjects.slice()
var originalIndex = newUnselected.indexOf(obj);
newUnselected.splice(originalIndex, 1)
var newFilteredUnselected = this.state.filteredUnselectedObjects.slice()
newFilteredUnselected.splice(index, 1)
this.setState({
selectedObjects: newSelected,
unselectedObjects: newUnselected,
filteredUnselectedObjects: newFilteredUnselected
});
}
handleUnselect = (index) => {
var obj = this.state.selectedObjects[index]
var newUnselected = this.state.unselectedObjects.slice()
newUnselected.push(obj)
var newFilteredUnselected = this.state.filteredUnselectedObjects.slice()
if(obj.name.toLowerCase().search(this.state.value.toLowerCase()) !== -1)
{
newFilteredUnselected.push(obj)
}
var newSelected = this.state.selectedObjects.slice()
newSelected.splice(index, 1)
this.setState({
unselectedObjects: newUnselected,
selectedObjects: newSelected,
filteredUnselectedObjects: newFilteredUnselected
});
}
render() {
return(
<div>
<input type="text" onChange={this.filterList}/>
<MultiSelect objects={this.state.filteredUnselectedObjects} onClickFunction={this.handleSelect}/>
<MultiSelect objects={this.state.selectedObjects} onClickFunction={this.handleUnselect}/>
</div>
);
}
}
const PLAYERS =[
{name: 'Charlie', id: 1},
{name: 'David', id: 2},
{name: 'Eric', id: 3},
{name: 'Emily', id: 4},
{name: 'Peter', id: 5},
{name: 'Sam', id: 6},
{name: 'Doug', id: 7}
]
React.render(<DoubleMultiselect objects={PLAYERS}/>, document.getElementById('app'));
beginner form react.js jsx
add a comment |
up vote
1
down vote
favorite
up vote
1
down vote
favorite
I am new to react (and javascript in general) but am enjoying learning it.
I have decided to make a little multiselect component which should work as follows:
I start with my data in one list, when I select an item from the list it goes into a second list (I can 'unselect' as well).
I also can filter the first list with a text input.
Before you review, I realize that there are likely existing solutions for this functionality that I could use, but I am just doing this to learn.
codepen here https://codepen.io/str8wavedave/pen/ZqWXQp
class MultiSelect extends React.Component {
render(){
return(
<ul>
{this.props.objects.map((object, index) => <li onClick = {() => this.props.onClickFunction(index)}>{object.name}</li>)}
</ul>
);
}
}
class DoubleMultiselect extends React.Component {
state = {
value: '',
filteredUnselectedObjects: this.props.objects,
unselectedObjects: this.props.objects,
selectedObjects: ,
}
filterList = (event) => {
var updatedList = this.state.unselectedObjects;
updatedList = updatedList.filter(function(item){
return item.name.toLowerCase().search(
event.target.value.toLowerCase()) !== -1;
});
this.setState({
filteredUnselectedObjects: updatedList,
value: event.target.value
})
}
handleSelect = (index) => {
var obj = this.state.filteredUnselectedObjects[index]
var newSelected = this.state.selectedObjects.slice()
newSelected.push(obj)
var newUnselected = this.state.unselectedObjects.slice()
var originalIndex = newUnselected.indexOf(obj);
newUnselected.splice(originalIndex, 1)
var newFilteredUnselected = this.state.filteredUnselectedObjects.slice()
newFilteredUnselected.splice(index, 1)
this.setState({
selectedObjects: newSelected,
unselectedObjects: newUnselected,
filteredUnselectedObjects: newFilteredUnselected
});
}
handleUnselect = (index) => {
var obj = this.state.selectedObjects[index]
var newUnselected = this.state.unselectedObjects.slice()
newUnselected.push(obj)
var newFilteredUnselected = this.state.filteredUnselectedObjects.slice()
if(obj.name.toLowerCase().search(this.state.value.toLowerCase()) !== -1)
{
newFilteredUnselected.push(obj)
}
var newSelected = this.state.selectedObjects.slice()
newSelected.splice(index, 1)
this.setState({
unselectedObjects: newUnselected,
selectedObjects: newSelected,
filteredUnselectedObjects: newFilteredUnselected
});
}
render() {
return(
<div>
<input type="text" onChange={this.filterList}/>
<MultiSelect objects={this.state.filteredUnselectedObjects} onClickFunction={this.handleSelect}/>
<MultiSelect objects={this.state.selectedObjects} onClickFunction={this.handleUnselect}/>
</div>
);
}
}
const PLAYERS =[
{name: 'Charlie', id: 1},
{name: 'David', id: 2},
{name: 'Eric', id: 3},
{name: 'Emily', id: 4},
{name: 'Peter', id: 5},
{name: 'Sam', id: 6},
{name: 'Doug', id: 7}
]
React.render(<DoubleMultiselect objects={PLAYERS}/>, document.getElementById('app'));
beginner form react.js jsx
I am new to react (and javascript in general) but am enjoying learning it.
I have decided to make a little multiselect component which should work as follows:
I start with my data in one list, when I select an item from the list it goes into a second list (I can 'unselect' as well).
I also can filter the first list with a text input.
Before you review, I realize that there are likely existing solutions for this functionality that I could use, but I am just doing this to learn.
codepen here https://codepen.io/str8wavedave/pen/ZqWXQp
class MultiSelect extends React.Component {
render(){
return(
<ul>
{this.props.objects.map((object, index) => <li onClick = {() => this.props.onClickFunction(index)}>{object.name}</li>)}
</ul>
);
}
}
class DoubleMultiselect extends React.Component {
state = {
value: '',
filteredUnselectedObjects: this.props.objects,
unselectedObjects: this.props.objects,
selectedObjects: ,
}
filterList = (event) => {
var updatedList = this.state.unselectedObjects;
updatedList = updatedList.filter(function(item){
return item.name.toLowerCase().search(
event.target.value.toLowerCase()) !== -1;
});
this.setState({
filteredUnselectedObjects: updatedList,
value: event.target.value
})
}
handleSelect = (index) => {
var obj = this.state.filteredUnselectedObjects[index]
var newSelected = this.state.selectedObjects.slice()
newSelected.push(obj)
var newUnselected = this.state.unselectedObjects.slice()
var originalIndex = newUnselected.indexOf(obj);
newUnselected.splice(originalIndex, 1)
var newFilteredUnselected = this.state.filteredUnselectedObjects.slice()
newFilteredUnselected.splice(index, 1)
this.setState({
selectedObjects: newSelected,
unselectedObjects: newUnselected,
filteredUnselectedObjects: newFilteredUnselected
});
}
handleUnselect = (index) => {
var obj = this.state.selectedObjects[index]
var newUnselected = this.state.unselectedObjects.slice()
newUnselected.push(obj)
var newFilteredUnselected = this.state.filteredUnselectedObjects.slice()
if(obj.name.toLowerCase().search(this.state.value.toLowerCase()) !== -1)
{
newFilteredUnselected.push(obj)
}
var newSelected = this.state.selectedObjects.slice()
newSelected.splice(index, 1)
this.setState({
unselectedObjects: newUnselected,
selectedObjects: newSelected,
filteredUnselectedObjects: newFilteredUnselected
});
}
render() {
return(
<div>
<input type="text" onChange={this.filterList}/>
<MultiSelect objects={this.state.filteredUnselectedObjects} onClickFunction={this.handleSelect}/>
<MultiSelect objects={this.state.selectedObjects} onClickFunction={this.handleUnselect}/>
</div>
);
}
}
const PLAYERS =[
{name: 'Charlie', id: 1},
{name: 'David', id: 2},
{name: 'Eric', id: 3},
{name: 'Emily', id: 4},
{name: 'Peter', id: 5},
{name: 'Sam', id: 6},
{name: 'Doug', id: 7}
]
React.render(<DoubleMultiselect objects={PLAYERS}/>, document.getElementById('app'));
beginner form react.js jsx
beginner form react.js jsx
edited Oct 4 at 12:48
200_success
127k15148412
127k15148412
asked Oct 4 at 7:15
Str8WaveDave
82
82
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
up vote
1
down vote
accepted
Overall this is good, but there are few things we can do improve this and make it more "modular".
Lets say someone wants to use your component and instead of an array of player objects, they have just an array of player names, this introduces a couple of problems.
- The prop to pass this in the list is called
objects
which doesn't reflect the type of data that they have and their list is long so they can't manually change it. - Your component is expecting the data passed in
objects
to be an array of objects.
How can we resolve this?
Lets first rename the prop from objects -> data
, this is better because it generalizes what this prop receives and the person using this can possibly assume you are able to handle multiple data types.
Next, we should update places where we are assuming the data to be an object and instead check to see if it is an object before handling it. One example is in <MultiSelect />
, where you are iterating over each item. We can change that to be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item.name : item}
</li>
))}
</ul>
);
}
}
Now we can display data from an array of strings!
But what if the person using your component was using an array of objects? Unfortunately there is no documentation so when he created the objects he used the property name firstName
instead of name
. This now breaks the code we re-wrote above, but we can fix it...
Lets set a new prop on <DoubleMultiselect />
called something like nameField
, which would be a string that is equal to the object property to display in the list.
So now wherever we were accessing the name
property of the player we can replace it with this.props.nameField
, re-writing the code above can now be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item[this.props.nameField]: item}
</li>
))}
</ul>
);
}
}
We've now added support for dynamic data, allowing anyone with any type of data to use this!
add a comment |
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
1
down vote
accepted
Overall this is good, but there are few things we can do improve this and make it more "modular".
Lets say someone wants to use your component and instead of an array of player objects, they have just an array of player names, this introduces a couple of problems.
- The prop to pass this in the list is called
objects
which doesn't reflect the type of data that they have and their list is long so they can't manually change it. - Your component is expecting the data passed in
objects
to be an array of objects.
How can we resolve this?
Lets first rename the prop from objects -> data
, this is better because it generalizes what this prop receives and the person using this can possibly assume you are able to handle multiple data types.
Next, we should update places where we are assuming the data to be an object and instead check to see if it is an object before handling it. One example is in <MultiSelect />
, where you are iterating over each item. We can change that to be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item.name : item}
</li>
))}
</ul>
);
}
}
Now we can display data from an array of strings!
But what if the person using your component was using an array of objects? Unfortunately there is no documentation so when he created the objects he used the property name firstName
instead of name
. This now breaks the code we re-wrote above, but we can fix it...
Lets set a new prop on <DoubleMultiselect />
called something like nameField
, which would be a string that is equal to the object property to display in the list.
So now wherever we were accessing the name
property of the player we can replace it with this.props.nameField
, re-writing the code above can now be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item[this.props.nameField]: item}
</li>
))}
</ul>
);
}
}
We've now added support for dynamic data, allowing anyone with any type of data to use this!
add a comment |
up vote
1
down vote
accepted
Overall this is good, but there are few things we can do improve this and make it more "modular".
Lets say someone wants to use your component and instead of an array of player objects, they have just an array of player names, this introduces a couple of problems.
- The prop to pass this in the list is called
objects
which doesn't reflect the type of data that they have and their list is long so they can't manually change it. - Your component is expecting the data passed in
objects
to be an array of objects.
How can we resolve this?
Lets first rename the prop from objects -> data
, this is better because it generalizes what this prop receives and the person using this can possibly assume you are able to handle multiple data types.
Next, we should update places where we are assuming the data to be an object and instead check to see if it is an object before handling it. One example is in <MultiSelect />
, where you are iterating over each item. We can change that to be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item.name : item}
</li>
))}
</ul>
);
}
}
Now we can display data from an array of strings!
But what if the person using your component was using an array of objects? Unfortunately there is no documentation so when he created the objects he used the property name firstName
instead of name
. This now breaks the code we re-wrote above, but we can fix it...
Lets set a new prop on <DoubleMultiselect />
called something like nameField
, which would be a string that is equal to the object property to display in the list.
So now wherever we were accessing the name
property of the player we can replace it with this.props.nameField
, re-writing the code above can now be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item[this.props.nameField]: item}
</li>
))}
</ul>
);
}
}
We've now added support for dynamic data, allowing anyone with any type of data to use this!
add a comment |
up vote
1
down vote
accepted
up vote
1
down vote
accepted
Overall this is good, but there are few things we can do improve this and make it more "modular".
Lets say someone wants to use your component and instead of an array of player objects, they have just an array of player names, this introduces a couple of problems.
- The prop to pass this in the list is called
objects
which doesn't reflect the type of data that they have and their list is long so they can't manually change it. - Your component is expecting the data passed in
objects
to be an array of objects.
How can we resolve this?
Lets first rename the prop from objects -> data
, this is better because it generalizes what this prop receives and the person using this can possibly assume you are able to handle multiple data types.
Next, we should update places where we are assuming the data to be an object and instead check to see if it is an object before handling it. One example is in <MultiSelect />
, where you are iterating over each item. We can change that to be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item.name : item}
</li>
))}
</ul>
);
}
}
Now we can display data from an array of strings!
But what if the person using your component was using an array of objects? Unfortunately there is no documentation so when he created the objects he used the property name firstName
instead of name
. This now breaks the code we re-wrote above, but we can fix it...
Lets set a new prop on <DoubleMultiselect />
called something like nameField
, which would be a string that is equal to the object property to display in the list.
So now wherever we were accessing the name
property of the player we can replace it with this.props.nameField
, re-writing the code above can now be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item[this.props.nameField]: item}
</li>
))}
</ul>
);
}
}
We've now added support for dynamic data, allowing anyone with any type of data to use this!
Overall this is good, but there are few things we can do improve this and make it more "modular".
Lets say someone wants to use your component and instead of an array of player objects, they have just an array of player names, this introduces a couple of problems.
- The prop to pass this in the list is called
objects
which doesn't reflect the type of data that they have and their list is long so they can't manually change it. - Your component is expecting the data passed in
objects
to be an array of objects.
How can we resolve this?
Lets first rename the prop from objects -> data
, this is better because it generalizes what this prop receives and the person using this can possibly assume you are able to handle multiple data types.
Next, we should update places where we are assuming the data to be an object and instead check to see if it is an object before handling it. One example is in <MultiSelect />
, where you are iterating over each item. We can change that to be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item.name : item}
</li>
))}
</ul>
);
}
}
Now we can display data from an array of strings!
But what if the person using your component was using an array of objects? Unfortunately there is no documentation so when he created the objects he used the property name firstName
instead of name
. This now breaks the code we re-wrote above, but we can fix it...
Lets set a new prop on <DoubleMultiselect />
called something like nameField
, which would be a string that is equal to the object property to display in the list.
So now wherever we were accessing the name
property of the player we can replace it with this.props.nameField
, re-writing the code above can now be:
class MultiSelect extends React.Component {
render() {
return (
<ul>
{this.props.data.map((item, index) => (
<li onClick={() => this.props.onClickFunction(index)}>
{typeof item === "object" ? item[this.props.nameField]: item}
</li>
))}
</ul>
);
}
}
We've now added support for dynamic data, allowing anyone with any type of data to use this!
answered 2 days ago
ByteSettlement
132210
132210
add a comment |
add a comment |
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Some of your past answers have not been well-received, and you're in danger of being blocked from answering.
Please pay close attention to the following guidance:
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f204915%2freact-multi-select-like-component-with-filter%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown