I want to check only the clicked Switch, but if i click on a Switch, all Switches are toggle.
class LoadMeister extends Component {
constructor(props) {
super(props);
this.state = {
daten: [],
isLoading: true,
checked: false
};
this.userUpdate = this.userUpdate.bind(this);
}
userUpdate(checked) {
this.setState({ checked });
}
[...]
render() {
const ListeUser = this.state.daten.map(meister => (
<Col md={2} xs={3} sm={3} className="karten">
<h3
title={meister.SD_Vorname}
style={{
fontWeight: "bold",
textOverflow: "ellipsis",
overflow: "hidden",
whiteSpace: "nowrap"
}}
>
{meister.SD_Vorname} {meister.SD_Nachname}
</h3>
<hr />
<p />
<FaBeer size="2em" />
<FaBeer size="2em" style={{ float: "right" }} />
<div style={{ marginTop: "10px" }}>
<Switch
key={meister.SD_Emplid}
width={151}
onChange={this.userUpdate}
checked={this.state.checked}
offColor="#A5C9D7"
onColor="#00556A"
/>
</div>
</Col>
));
[Return()]
xxxxxxxxxxxxxxxxxxxxxxxxxx
So what i have to do, so that only the clicked Switch is Toggle ?
I use the react-switch Lib.
xxxxxxxxxxxxxxxxxxxxxxxxxx
Try something like:
userUpdate(checkedId) {
this.setState({ checkedId:id });
}
...rest of your code
<Switch
key={meister.SD_Emplid}
width={151}
onChange={()=>this.userUpdate(meister.SD_Emplid)}
checked={this.state.checkedId===meister.SD_Emplid}
offColor="#A5C9D7"
onColor="#00556A"
/>
Yes indeed, all switches are toggled because you have just one value in your state for all switches : this.state.checked.
You need to make one value for each switch, you can use an array or an object for this :
With an array
// constructor
this.state = {
checked: [] // all switches
};
// Update the checked state at the right index
userUpdate(checked, index) {
const newChecked = this.state.checked
newChecked[index] = checked
this.setState({ checked: newChecked });
}
// render
const ListeUser = this.state.daten.map((meister,index) => (
// More code
<Switch
key={meister.SD_Emplid}
width={151}
onChange={(checked) => this.userUpdate(checked, index)} // We need to know which index is clicked
checked={this.state.checked[index]}
offColor="#A5C9D7"
onColor="#00556A"
/>
)
With an object:
// constructor
this.state = {
checked: {} // all switches, keys will be the daten ids
};
// Update the checked state
userUpdate(checked, id) {
const newChecked = this.state.checked
newChecked[id] = checked
this.setState({ checked: newChecked });
}
// render
const ListeUser = this.state.daten.map(meister => (
// More code
<Switch
key={meister.SD_Emplid}
width={151}
onChange={(checked) => this.userUpdate(checked, meister.SD_Emplid)}
checked={this.state.checked[meister.SD_Emplid]}
offColor="#A5C9D7"
onColor="#00556A"
/>
)
The two solutions are very similar.
You use the checked state for all of your Switch components, which results in the behaviour you experience.
A solution would be to save checked in your meister objects:
userUpdate(index, checked) {
const { daten } = this.state;
daten[index].checked = checked;
this.setState({ daten });
}
/* inside your render */
this.state.daten.map((meister, index) => (
/* ... */
<Switch
{/* ... */}
checked={meister.checked}
onChange={this.userUpdate.bind(this, index)}
/>
));
I use the second argument of .bind to add index as the first argument to the userUpdate function.
Related
I have search filter and categories. I just want to have a possibility to reset state in single page application.
Due to React.js I guess I do everything correct to pass state from parent to child and then from child to parent. But, unfortunately, something is going wrong. I tried a lot and what I discovered, that onAddCategory() in DropdownGroup doesn't update current state.
Sorry in advance, I add whole code, my be something there could affect this. But i guess you can see first halfs of two codes and it will be enough.
Thank you in advance.
I have parent component:
class DropdownGroup extends React.Component {
constructor(props) {
super(props);
this.state = {
categories: [], // we have empty array, that pass to CategoryDropdown
};
this.onAddCategory = this.onAddCategory.bind(this);
}
onAddCategory(newCategory) {
this.setState(() => ({
categories: newCategory,
}));
}
onSelectCategory(path) {
this.props.onChangeEvents(path);
}
render() {
const months = ['January', 'February' ... ];
const eventsType = ['Party', 'Karaoke ... ];
const { categories } = this.state;
return (
<ButtonToolbar className="justify-content-center pb-4 pt-4">
{ console.log(categories) }
<CategoryDropdown
items={eventsType}
homePath="events"
path="events/categories/"
categories={categories} // here we pass our empty array (or updated later)
addCategories={this.onAddCategory} // this is what helps to update our array
onApply={(path) => this.onSelectCategory(path)}
/>
<MyDropdown
id="sort-by-month"
name="By month"
items={months}
onSelect={(e) => this.onSelectCategory(`events/month/${e}`)}
/>
<DropdownWithDate
oneDate="events/date/"
rangeDate="events/dates?from="
onApply={(path) => this.onSelectCategory(path)}
/>
<Button
onClick={() => this.setState({ categories: [] })} // here we can reset the value of our array
className="m-button ml-5"
>
Reset
</Button>
</ButtonToolbar>
);
}
}
DropdownGroup.propTypes = {
onChangeEvents: PropTypes.any.isRequired,
};
export default DropdownGroup;
and this is child component
class CategoryDropdown extends Component {
constructor(props) {
super(props);
this.state = {
visible: false,
selected: this.props.categories, // here we get values from props (now empty, then updated values)
};
this.onVisibleChange = this.onVisibleChange.bind(this);
}
onVisibleChange(visible) {
this.setState({
visible: visible,
});
}
saveSelected(selectedKeys) {
this.setState({
selected: selectedKeys,
});
}
addCategories() {
this.props.addCategories(this.state.selected); // here props are updated
}
confirm() {
const { selected } = this.state;
this.addCategories(this.state.selected);
const { homePath, path } = this.props;
if (selected.length > 0) {
this.props.onApply(path + selected);
} else {
this.props.onApply(homePath);
}
this.onVisibleChange(false);
}
render() {
const { visible } = this.state;
const { items } = this.props;
const menu = (
<Menu
multiple
onSelect={(e) => { this.saveSelected(e.selectedKeys); }}
onDeselect={(e) => { this.saveSelected(e.selectedKeys); }}
>
{items.map((item) => (
<MenuItem
key={item.replace('\u0020', '\u005f').toLowerCase()}
>
{item}
</MenuItem>
))}
<Divider />
<MenuItem disabled>
<Container
className="text-center "
style={{
cursor: 'pointer',
pointerEvents: 'visible',
}}
onClick={() => {
this.confirm();
}}
>
Select
</Container>
</MenuItem>
</Menu>
);
return (
<Dropdown
trigger={['click']}
onVisibleChange={this.onVisibleChange}
visible={visible}
closeOnSelect={false}
overlay={menu}
>
<Button className="m-button">By Category</Button>
</Dropdown>
);
}
}
CategoryDropdown.propTypes = {
onApply: PropTypes.any.isRequired,
items: PropTypes.any.isRequired,
path: PropTypes.string.isRequired,
homePath: PropTypes.string.isRequired,
categories: PropTypes.array.isRequired,
addCategories: PropTypes.any.isRequired,
};
export default CategoryDropdown;
I want to display logged use name at a left section on the page.
When i try to reach user displayName it says that it can't fin value of undefined. Can someone explain me why?
I have tried componentDidMount and some other methods, but it seems that something is wrong during render here
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
// If firebase has detect a user
if (user) {
this.props.setUser(user);
this.props.history.push("/");
Here is the file
class UserPanel extends React.Component {
state = {
user: null
}
componentDidMount(){
this.setState({ user: this.props.currentUser })
}
dropdownOptions = () => [
{
key: "user",
text: (
<span>
Sign in as <strong>{this.state.user && this.state.user.displayName}</strong>
</span>
),
disabled: true
},
{
key: "avatar",
text: <span>Change Avatar</span>
},
{
key: "signout",
// Set a signout Function to enable user to sign out of the chat
text: <span onClick={event => this.handleSignOut(event)}>SignOut</span>
}
];
handleSignOut = (event) => {
// You need to prevent form submission. Use event.preventDefault() in your handle submit function.
event.preventDefault();
firebase
.auth()
.signOut()
.then(() => console.log("See you"));
}
render(){
console.log(this.props.currentUser);
// !this.state.currentUser ? <Spinner /> :
return (
<Grid style={{ background: '#4c3c4c' }}>
<Grid.Column>
<Grid.Row style={{ padding: '1.2rem', margin: 0 }}>
<Header inverted floated='left' as='h2'>
<Icon name='code' />
<Header.Content>VirtualChat</Header.Content>
</Header>
</Grid.Row>
{/* User Dropdown Choices */}
<Header style={{ padding: "0.25em" }} as="h4" inverted>
<Dropdown
trigger={<span>{this.state.user.displayName}</span>}
options={this.dropdownOptions()}
/>
</Header>
</Grid.Column>
</Grid>
)
}
}
const mapStateToProps = state => ({
currentUser: state.user.currentUser
})
export default connect(mapStateToProps)(UserPanel);
In your code, this.state.user is not a truthy-value until after componentDidMount(), which doesn't take affect until after the first render. During the initial-render, you are trying to access the value within an object that does not yet exist.
What you could do is implement a condition to check whether this.state.user is valid, otherwise display an empty string. The speed in which the component re-renders is very fast, so you wouldn't even notice the empty-string on first-render.
<Header style={{ padding: "0.25em" }} as="h4" inverted>
<Dropdown
trigger={<span>{this.state.user ? this.state.user.displayName : ""}</span>}
options={this.dropdownOptions()}
/>
</Header>
I want to display an input field for every click on a button "Add Input Field". Now it is showing only one time when I click on button "Add Input Field". How Can I achieve this.
Here below I have created a codesandbox.
https://codesandbox.io/s/6lr8w994vn
To achieve this, you will have to store an array in your state containing the information to generate each field.
Then, in your add function, set your array to your deconstructed previous array, and your additional element:
class InputAdder extends React.Component {
constructor(props) {
super(props)
this.state = {
inputs: []
}
}
addInput = ev => {
this.setState(prev => ({ inputs: [...prev.inputs, 'Hi'] }))
}
render() {
return (
<div>
<button onClick={this.addInput}>Add input</button>
{this.state.inputs.map(node => <input type="text"/>)}
</div>
)
}
}
ReactDOM.render(<InputAdder/>, document.getElementById('root'))
input {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.5.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.5.2/umd/react-dom.production.min.js"></script>
<div id='root'>
You will then simply have to map over your array in your render function
One way is to store the number of inputs in the state, which will be incremented by one on every click. Then pass this value to props of your component and use map function to create the corresponding number of inputs.
To make this work, Change your state from this:
this.state = {
add: false,
addChild: false
};
to :
this.state = {
add: [],
addChild: []
};
and in addInputField function increase add array size every time it clicked, then use this.state.add to render your input fields. Same applies to Child.
Demo Component:
class Demo extends Component {
constructor() {
super();
this.state = {
add: [],
addChild: []
};
}
addInputField = event => {
const add = this.state.add;
const size = add.length + 1;
add.push(size);
this.setState({
add
});
event.preventDefault();
};
addChildInputField = event => {
const addChild = this.state.addChild;
const size = addChild.length + 1;
addChild.push(size);
this.setState({
addChild
});
event.preventDefault();
};
handleChange = event => {
this.setState({
[event.target.name]: event.target.value
});
};
render() {
return (
<div>
<Grid>
<Grid>
<Button onClick={this.addInputField} style={{ marginTop: 30 }}>
{" "}
<Icon>
<AddCircle />
</Icon>{" "}
Add Input Field
</Button>
{this.state.add.map(index => {
return (
<Grid>
<TextField
id="preference"
label="Preference"
name="points"
value={this.state.name}
onChange={this.handleChange}
margin="normal"
/>
<Button onClick={this.addChildInputField}> Add Child</Button>
</Grid>
);
})}
{this.state.addChild.map(index => {
return (
<Grid style={{ marginLeft: 50 }}>
<TextField
id="preference"
label="Child Preference"
name="points"
value={this.state.name}
onChange={this.handleChange}
margin="normal"
/>
</Grid>
);
})}
</Grid>
<Grid>
<Button
color="primary"
variant="contained"
type="submit"
style={{ margin: "0.2rem", marginTop: 30 }}
>
Save
</Button>
</Grid>
</Grid>
</div>
);
}
}
export default Demo;
Full code available here
Hope it helps.
I am trying to use react-select. I have certain condition (boolean), when the param is true, some property/attribute of the react-select would change base on the logic. One if them is menuList. My objective, if the param is true, I want the menuList displayed and searchable, but when false, I want the menuList hidden but still available (not disabled, thats why I use onChange and onInputChange prop). Here is what I've set so far:
const isExist = true;
return (
<div style={{ width: '50%', margin: 20 }}>
<Select
id="asd"
value={selectedOption}
onChange={isExist ? this.handleChange : null}
onInputChange={isExist ? null : e => this.tests(e) }
options={options}
isClearable={true}
styles={style}
placeholder="Please type"
noOptionsMessage={() => isExist ? 'Zero Result' : null}
components={{ MenuList: () => isExist ? 'display the menu but how?' : null }}
/>
</div>
);
any help would be helpful. Thank you!
Based on the behaviour you describe you could use a controlled menuIsOpen props like this:
class App extends Component {
constructor(props) {
super(props);
this.state = {
isExist: false,
menuIsOpen: false,
selectedOption: null
};
}
onChange = e => {
this.setState({
selectedOption: e
});
};
onInputChange = (options, { action }) => {
if (this.state.isExist) {
if (action === "menu-close") {
this.setState({ menuIsOpen: false });
}
} else {
if (action === "input-change") {
// do whatever you want with the entered value
}
}
};
onFocus = e => {
if (this.state.isExist) this.setState({ menuIsOpen: true });
};
render() {
return (
<div style={{ width: "50%", margin: 20 }}>
<Select
id="asd"
value={this.state.selectedOption}
onFocus={this.onFocus}
onChange={this.onChange}
onInputChange={this.onInputChange}
options={options}
isClearable={true}
placeholder="Please type"
noOptionsMessage={() => (this.state.isExist ? "Zero Result" : null)}
menuIsOpen={this.state.menuIsOpen}
/>
</div>
);
}
}
Here a live example.
I'm trying to use the react-select component as an input and a select component.
Doing so I would like to prevent the menu to open while the user is typing the input.
I just can't find a way to update this behavior by either a prop or updating the onInputChange method.
My problem if I decide to use a controlled state with the menuIsOpen prop is that I can't manage to reopen the Menu control is clicked.
Here is what I have so far, do you guys any idea of how this could be achieved ?
<StyledSelect
components={{ IndicatorSeparator }}
{..._.omit(this.props, [])}
filterOption={() => true}
inputValue={inputValue}
onChange={value => {
this.select.setState({ menuIsOpen: false });
this.setState({ selectValue: value });
}}
onInputChange={(value, event) => {
if (event && event.action === 'input-change') {
this.setState({ inputValue: value });
}
}}
openMenuOnClick={false}
/>
Example
I think you are in the right direction using onInputChange and I would add a controlled menuIsOpen props like the following code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
menuIsOpen: false
};
}
openMenu = () => {
this.setState({ menuIsOpen: !this.state.menuIsOpen });
};
onInputChange = (props, { action }) => {
if (action === "menu-close") this.setState({ menuIsOpen: false });
};
render() {
const DropdownIndicator = props => {
return (
components.DropdownIndicator && (
<div
onClick={this.openMenu}
style={{ display: "flex", alignItems: "center" }}
>
<span style={{ marginLeft: 12 }}>kg</span>
<components.DropdownIndicator
{...props}
onClick={() => {
console.log("ici");
}}
/>
</div>
)
);
};
return (
<Select
components={{ DropdownIndicator }}
options={options}
onChange={this.onChange}
onInputChange={this.onInputChange}
menuIsOpen={this.state.menuIsOpen}
/>
);
}
}
The idea with this code is to fire an onClick event on the DropdownIndicator custom component.
Here a live example.