To be able to add the headers dynamically using a React Select component, I overrode the ToolBar of MaterialTable.
The override worked, but now when I select more than 3 checkboxes and try to update the columns in MaterialTable's datamanger, the app terminates/freezes.
(The documentation of MaterialTable didn't help me much with the implementation, so I had to work through the code to update the columns. That's why I'm not sure if I'm doing this correctly. Furthermore this is only a rough first implementation.).
I would appreciate any tips on this. Thanks in advance ^^
The following code is inside the overridden Material-Table ToolBar componente. I simply "copied" the implementation of Material-Table and pasted the React-Select component into the ToolBar render method.
handleChange = (selected) => {
let columns = this.props.dataManager.columns;
for (let index in columns) {
columns[index].hidden = false;
}
for (let index in selected) {
let cIndex = columns.findIndex(object => {
return object.title === selected[index].value;
});
if (cIndex !== -1) {
columns[cIndex].hidden = true;
}
}
this.setState({
optionSelected: selected,
});
this.props.dataManager.setColumns(columns); //?
this.props.onColumnsChanged(columns); //Is this correct?
}
renderSelectHeaders() {
let columns = this.props.columns.map((columns) => ({label: columns.title, value: columns.title}))
return(
<span
class="d-inline-block"
data-toggle="popover"
data-trigger="focus"
data-content="Please selecet columns"
>
<ReactSelect
options={columns}
isMulti
placeholder={"Selected Columns"}
closeMenuOnSelect={false}
hideSelectedOptions={false}
components={{
Option
}}
onChange={this.handleChange}
allowSelectAll={true}
value={this.state.optionSelected}
className={"select"}
/>
</span>);
}
Related
I'm trying to create a filter button by ReactJs, spent a lot of times but still do not know why it's doesn't work
Here are my codePen: https://codepen.io/tinproht123/pen/gOxeWpy?editors=0110
const [menuItems, setMenuItems] = React.useState(menu);
const [categories, setCategories] = React.useState(allCategories);
const filterItems = (category) =>{
if(category === 'all'){
setMenuItems(menu);
return;
}
const newItems = menu.filter((item)=> item.category === category)
setMenuItems(newItems);
}
return(
<section className='container'>
<div className='title'>
<h1>Our menu</h1>
</div>
<Categories categories={categories} filterItems={filterItems}/>
<Menu menu={menuItems}/>
</section>
)
}~~~
I checked your code and the problem isn't in the part that you showed to us.
Instead please check your codes 103th line, on codepen. Your code seems like that:
const Menu = () =>{
return(
<div className='menu-container'>
{menu.map((menuItem) => {
....
Be careful to the first line, since your all menu items stays in menu variable, even though you made correct filtering, you're printing the result for all menus.
I saw that you're sending a prop to a <Menu menu={menuItems}>....
but you're not using it. To use this prop you should add a parameter to your Menu function;
const Menu = ({menu}) =>{
return(
<div className='menu-container'>
{menu.map((menuItem) => {
Just like above.
In the Menu component, you're not passing any props but just rendering the const value declared on top of the file.
You're filtering exactly by categories but rendering is not showing the updated one. :)
I have modified a multi react-select component with ValueContainer so that if several options are selected there is only one option shown and in parenthesis the number of additionally selected options. The code looks like this:
ValueContainer = ({ children, getValue, ...props }) => {
let values = getValue();
let valueLabel = "";
if (values.length > 0) valueLabel += props.selectProps.getOptionLabel(values[0]);
if (values.length > 1) valueLabel += ` (${values.length})`;
// Keep standard placeholder and input from react-select
let childrenToRender = React.Children.toArray(children).filter((child) => ['Input', 'DummyInput', 'Placeholder'].indexOf(child.type.name) >= 0);
return (
<components.ValueContainer {...props}>
{!props.selectProps.inputValue && valueLabel }
{ childrenToRender }
</components.ValueContainer>
);
};
customStyles = {
valueContainer: (provided, state) => ({
...provided,
textOverflow: "ellipsis",
//maxWidth: "90%",
//whiteSpace: "nowrap",
overflow: "hidden",
display: "initial"
})
};
//inside render()
const employeeFilter = (
<div className="multi-select col-6 col-md-3 filter-element btn">
<span className="filter-label">Filter by employee(s)</span>
<Select
options={this.props.employeeProps.employeeData}
onChange={this.handleEmployeeChange}
value={selectedEmployee}
isMulti={true}
inputId='clickableInput'
components={{
ValueContainer
}}
closeMenuOnSelect={false}
hideSelectedOptions={false}
styles={customStyles}
isSearchable={false}
/>
</div>
);
ValueContainer and customStyles are defined before the React Component and it works fine on my development machine. However, on production it does not work properly:
clicking outside of the dropdown does not close it (as on the dev machine)
the placeholder text is barely visible (the area has very little height)
The reason for this seems to be the minification process. I am using TerserPlugin within webpack. Using UglifyJS is not an option since I am writing ES6 code (recommendation here: Why would Webpack 4 minification prevent styles for react-select component?)
I tried everything within this thread https://github.com/JedWatson/react-select/issues/2597 but couldn't get it to work. Might be my lack of understanding of how the components system in react-select works (I took the code from an example).
Thanks for providing any help!
This is how I am currently doing it - it is very uneloquent, and it doesn't work. I am getting
TypeError: Cannot read property '0' of undefined
at grid.push(<Card key={index+x} name={list[index+x][0]} img={list[index+x][1]}/>);
this is my code
const list = // my list of data
var grid = [];
list.map((element, index) => {
if (index%4 == 0) {
if (index<list.length-2) {
const el = (<div className="row">
//card being a component I built
<Card key={index} name={element[0]} img={element[1]}/>
<Card key={index+1} name={list[index+1][0]} img={list[index+1][1]}/>
<Card key={index+2} name={list[index+2][0]} img={list[index+2][1]}/>
<Card key={index+3} name={list[index+3][0]} img={list[index+3][1]}/>
</div>)
grid.push(el);
} else {
let remainder = (list.length-index);
for (var x=0;x+=1;x<remainder) {
grid.push(<Card key={index+x} name={list[index+x][0]} img={list[index+x][1]}/>);
}
}
}
})\
return (
<div>
{grid}
</div>
)
The current way I am doing it is making grid then iterating through the list and adding jsx to it. The problem is that I need to make a row for bootstrap every 4 columns to make the grid, any better ideas, solutions greatly appreciated!
You are using index+1, index+2, index+3 which will never exist in the array, when you reach last element.
Instead calculate it in some other variable to find elements which exist.
I have a react component with a <Select options={myoptions} onChange={this.mychangehandler}/> element from react-select. I have saved some settings using redux that can be accessed e.g. using this.props.myreducer.dropdownvalue(already tested, the values are available when first rendering the component)
Depending on the redux props I want to automatically select one of the options in the dropdown. How can I tell the <Select.../>dropdown to automatically select option 2 if this.props.myreducer.dropdownvalue is equal to 1?
There are countless properties but somehow I found no one that can help me automatically select the proper entry.
Any ideas? Solutions that will automatically trigger onChange as well prefered, but should be no problem if not.
Thanks in advance :)
Regards Christian
edit: Here is the full code:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import Auxiliary from '../../../../../../../hoc/Auxiliary/Auxiliary';
import classes from './Dropdown.module.css';
import Select from 'react-select';
class Dropdown extends Component {
changeSetting = (event) => {
console.log('qid'+this.props.qid)
console.log('event: '+JSON.stringify(event))
if(this.props.certs.certquestions[this.props.qid].red === event.id)
{
this.props.colorchanger("red")
}else if(this.props.certs.certquestions[this.props.qid].yellow === event.id)
{
this.props.colorchanger("yellow")
}else if(this.props.certs.certquestions[this.props.qid].green === event.id)
{
this.props.colorchanger("green")
}
}
render(){
const options = []
const qid = this.props.qid
console.log('qid:'+qid)
const question = this.props.certs.certquestions[qid]
const opt = question.options.split("|")
for(let i = 0;i<opt.length;i++){
let optname = opt[i]
options.push({value:i, label:optname, id:i})
}
let notes = "";
let selectedOption = null;
if(this.props.loadp)
{
let progress = this.props.certs.loadedProgress[this.props.users.users[this.props.users.currentuser].target_id+'_'+this.props.q]
if (typeof progress !== 'undefined')
{
notes = progress.notes
selectedOption = progress.selectedOption
}
}
return(
<Auxiliary>
<h3>{question.title}</h3>
<Select className = {classes.Dropdown} options={options} onChange={this.changeSetting}/ value={selectedOption}> //selectedOption is an Integer
<textarea value = {notes}/>
</Auxiliary>
)
}
}
const mapStateToProps = state => {
return {
certs: state.certs,
users: state.users
};
}
export default connect(mapStateToProps, null)(Dropdown);
the options have different labels and value and id from 0 to 9. I tried to add value={selectedOption} to the Select element, also tried it with the label instead, but it always only shows the "Select..." placeholder and doesn't select the option and trigger the onChange. Any idea what I'm doing wrong?
Solved: I now know what I did wrong, thanks a lot :) I needed to pass an object with value, id and label to value :)
I needed to pass an object like this: {"value":0,"label":"v1","id":0} to value
I'm using react-select in my project and I'm using it within a map like that:
renderItems() {
this.props.items.map(item => (
<Select
id="options"
value={this.state.optionSelected}
onChange={this.onChangeOption}
options={this.showOptions()}
/>
);
}
It show correctly all my options for all my items but now I can not get rid about the select...
Basically when I select an option on a single item, that option is changing for all items...
This is what i did so far:
onChangeOption(e) {
this.setState({ optionSelected: e.value });
}
How can I adjust it to change the option only on the one I wish to change it?
Thanks
You are using the same change handler for all of your select components and then set the same state value for all your select components. To deal with this either you need to separate your select components with a container component that handles their own state and change event or you need to give each select component a unique state value.
Example
renderItems() {
this.props.items.map(item => (
<Select
id="options"
value={this.state.optionSelected[item.id]}
onChange={(event) => this.onChangeOption(event, item.id)}
options={this.showOptions()}
/>
);
}
onChangeOption(event, itemId) {
this.setState((prevState) => {
const prevStateClone = Object.assign({}, prevState);
prevStateClone.optionSelected[itemId] = event.target.value;
return prevStateClone;
});
}
Instead of making optionSelected string variable, make it as array in state.
Now do the following.
renderItems() {
this.props.items.map(item, index => (
<Select
id="options"
value={this.state.optionSelected[index]}
onChange={(selectedValue) => this.onChangeOption(selectedValue, index)}
options={this.showOptions()}
/>
);
}
onChangeOption(selectedValue, index) {
const optionSelected = this.state.optionSelected.slice() // slicing to get new copy of optionSelected instead of referencing to old one which causes mutation
optionSelected[index] = selectedValue
this.setState({optionSelected: optionSelected})
}
What you were doing is using a single variable to hold values of the select box. So if anyone changes, it will reflect all select box
Try cloning the object to a new object or if this optionSelected is a class, you can implement a constructor that clones it for your like:
export class optionSelectedClass {
myfield = '';
constructor(fields){
this.myField = fields.myField;
}
}
or even
export class optionSelectedClass {
myfield = '';
constructor(fields){
for (let f in fields) {
if (!this[f]) {
this[f] = fields[f];
}
}
}
}