I have to change the Checkbox UI from Check to Uncheck and vice versa according to the value of the state : checked.
For that I have used 'react-native-elements'.
My state name is checked
I have also taken two Buttons to change the state value, but the checkbox is not rendering according to value of the state.
I have done as below : Please guide.
Thanks.
import React, {Component} from 'react';
import {Platform, StyleSheet, Text, View,Button,Alert} from 'react-native';
import { CheckBox } from 'react-native-elements'
export default class App extends Component {
constructor (props) {
super(props)
this.state = {
checked: true,
}
}
render() {
return (
<View style={styles.container}>
<CheckBox
title='Click Here'
checked={this.state.checked}
/>
<CheckBox
center
title='Click Here'
checked={this.state.checked}
/>
<Button value="Check"
title="Check"
onPress={()=>{
this.state.checked=true;
Alert.alert("State >>"+this.state.checked);
}}
/>
<Button value="Un-Check"
title="Un-Check"
onPress={()=>{
this.state.checked=false;
Alert.alert("State >"+this.state.checked);
}}
/>
</View>
);
}
}
EDIT
I have done as below :
<Button value="Check"
title="Check"
onPress={()=>{
this.setState((prevState,props)=>({
isChecked=prevState.isChecked
}));
//this.state.isChecked=true;
Alert.alert("State >>"+this.state.isChecked);
}}
/>
But, It gives me syntax error :
Unexpected Token (81:23)
replace
this.state.isChecked=true;
with
this.setState({
isChecked : true
})
You can not assign value directly to the state for that you have to use setState({}) so update you state as mentioned above
for false use
this.setState({
isChecked : false
})
For EDIT section
this.setState((prevState,props)=>({
isChecked : prevState.isChecked
}));
Use : inside stae do not use = it will give sintext error..
Your onPress in wrong. you need setState. In addition to #Jay 's Answer,
setState is asynchronous, meaning if you press the checkbox multiple timesm, things can get messed up.So the best way is to get the prevState and mutate it instead
For eg: Toggling the state to and fro like in a checkbox
this.setState((prevState, props) => ({
isChecked: prevState.isChecked
}));
Related
I am using React v18.1, react-bootstrap v2.4. I have a Modal component I am trying to get to display upon a button press. The modal component is quite simple:
class AdjustmentModal extends React.Component {
constructor(props) {
super(props);
this.state = {
'show': this.props.show
};
this.handleClose = this.handleClose.bind(this);
}
handleClose() {
this.setState({ show: false })
}
render() {
return (
<Modal show={this.state.show} onHide={this.handleClose}>
[ ... Modal Content Here ... ]
</Modal>
);
}
}
export default AdjustmentModal;
As you can see, I bind the modal's show property to the value of show in state.
Then, in the component in which I want to display my modal, I have the following:
// Within render() ...
<AdjustmentModal
show={this.state.showAdjustment}
partNo={this.state.partNo}
onHandQty={this.state.onHandQty}
/>
// Futher on in the code, display the modal on click:
<Button className="icon" onClick={this.handleDisplayAdjustment}>
<i className="bi bi-pencil-square"></i>
</Button>
handleDisplayAdjustment :
handleDisplayAdjustment(event) {
event.preventDefault();
this.setState({
showAdjustment : true
});
}
Now, despite the value showAdjustment in the parent component changing to true, the modal doesn't display.
I could set the <Modal show={this.props.show} .../> instead, but props are read-only, so there is no way to close the modal again if reading from props rather than state.
You can use props, which is a better way to handle this if you want to close it then pass a method from the parent which when called update the state in the parent to false and due state update the parent component will re render and though the child component that is the modal component and the Modal will get the updated value which will be false. below is the code on how you can achieve that.
closeModal() {
this.setState({
showAdjustment: false
})
}
// Within render() ...
<AdjustmentModal
show={this.state.showAdjustment}
partNo={this.state.partNo}
onHandQty={this.state.onHandQty}
onClose={this.closeModal.bind(this)}
/>
// Futher on in the code, display the modal on click:
<Button className="icon" onClick={this.handleDisplayAdjustment}>
<i className="bi bi-pencil-square"></i>
</Button>
For the child component
class AdjustmentModal extends React.Component {
handleClose() {
this.props.onClose()
}
render() {
return (
<Modal show={this.props.show} onHide={this.handleClose}>
[ ... Modal Content Here ... ]
</Modal>
);
}
}
export default AdjustmentModal;
EDIT: Explaining the approach
This will make your Modal component a Controlled component that is controlled by Parent, also updating props as a state inside the child component is not the right way, which may create potential bugs.
I made a code sandbox example for my problem: https://codesandbox.io/s/react-form-submit-problem-qn0de. Please try to click the "+"/"-" button on both Function Example and Class Example and you'll see the difference. On the Function Example, we always get the previous value while submitting.
I'll explain details about this example below.
We have a react component like this
function Counter(props) {
return (
<>
<button type="button" onClick={() => props.onChange(props.value - 1)}>
-
</button>
{props.value}
<button type="button" onClick={() => props.onChange(props.value + 1)}>
+
</button>
<input type="hidden" name={props.name} value={props.value} />
</>
);
}
It contains two buttons and a numeric value. User can press the '+' and '-' button to change the number. It also renders an input element so we can use it in a <form>.
This is how we use it
class ClassExample extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 1,
lastSubmittedQueryString: ""
};
this.formEl = React.createRef();
}
handleSumit = () => {
if (this.formEl.current) {
const formData = new FormData(this.formEl.current);
const search = new URLSearchParams(formData);
const queryString = search.toString();
this.setState({
lastSubmittedQueryString: queryString
});
}
};
render() {
return (
<div className="App">
<h1>Class Example</h1>
<form
onSubmit={event => {
event.preventDefault();
this.handleSumit();
}}
ref={ref => {
this.formEl.current = ref;
}}
>
<Counter
name="test"
value={this.state.value}
onChange={newValue => {
this.setState({ value: newValue }, () => {
this.handleSumit();
});
}}
/>
<button type="submit">submit</button>
<br />
lastSubmittedQueryString: {this.state.lastSubmittedQueryString}
</form>
</div>
);
}
}
We render our <Counter> component in a <form>, and want to submit this form right after we change the value of <Counter>. However, on the onChange event, if we just do
onChange={newValue => {
this.setState({ value: newValue });
this.handleSubmit();
}}
then we won't get the updated value, probably because React doesn't run setState synchronously. So instead we put this.handleSubmit() in the second argument callback of setState to make sure it is executed after the state has been updated.
But in the Function Example, as far as I know in state hooks there's nothing like the second argument callback function of setState. So we cannot achieve the same goal. We found out two workarounds but we are not satisfied with either of them.
Workaround 1
We tried to use the effect hook to listen when the value has been changed, we submit our form.
React.useEffect(() => {
handleSubmit();
}, [value])
But sometimes we need to just change the value without submitting the form, we want to invoke the submit event only when we change the value by clicking the button, so we think it should be put in the button's onChange event.
Workaround 2
onChange={newValue => {
setValue(newValue);
setTimeout(() => {
handleSubmit();
})
}}
This works fine. We can always get the updated value. But the problem is we don't understand how and why it works, and we never see people write code in this way. We are afraid if the code would be broken with the future React updates.
Sorry for the looooooooong post and thanks for reading my story. Here are my questions:
How about Workaround 1 and 2? Is there any 'best solution' for the Function Example?
Is there anything we are doing wrong? For example maybe we shouldn't use the hidden input for form submitting at all?
Any idea will be appreciated :)
Can you call this.handleSubmit() in componentDidUpdate()?
Since your counter is binded to the value state, it should re-render if there's a state change.
componentDidUpdate(prevProps, prevState) {
if (this.state.value !== prevState.value) {
this.handleSubmit();
}
}
This ensure the submit is triggered only when the value state change (after setState is done)
It's been a while. After reading React 18's update detail, I realize the difference is caused by React automatically batching state updates in function components, and the "official" way to get rid of it is to use ReactDOM.flushSync().
import { flushSync } from "react-dom";
onChange={newValue => {
flushSync(() => {
setValue(newValue)
});
flushSync(() => {
handleSubmit();
});
}}
I'm working with React MDL library, and I used pre-defined components like FABButton
<FABButton>
<Icon name="add"/>
</FABButton>
And it shows the button as in the image bellow:
Now, what I want is showing a dialog with the + icon... not as what happens here:
This happened after this code:
<FABButton>
<AddingProject />
<Icon name="add" />
</FABButton>
The class of dialog is as follows:
class AddingProject extends Component {
constructor(props) {
super(props);
this.state = {};
this.handleOpenDialog = this.handleOpenDialog.bind(this);
this.handleCloseDialog = this.handleCloseDialog.bind(this);
}
handleOpenDialog() {
this.setState({
openDialog: true
});
}
handleCloseDialog() {
this.setState({
openDialog: false
});
}
render() {
return (
<div>
<Button colored onClick={this.handleOpenDialog} raised ripple>
Show Dialog
</Button>
<Dialog open={this.state.openDialog} onCancel={this.handleCloseDialog}>
<DialogTitle>Allow data collection?</DialogTitle>
<DialogContent>
<p>
Allowing us to collect data will let us get you the information
you want faster.
</p>
</DialogContent>
<DialogActions>
<Button type="button">Agree</Button>
<Button type="button" onClick={this.handleCloseDialog}>
Disagree
</Button>
</DialogActions>
</Dialog>
</div>
);
}
}
export default AddingProject;
The above code is with the required import statements
This works with me....
First step: I added the component of the modal as follows:
<FABButton>
<Icon name="add" />
</FABButton>
<ProjectModal>
Second step: I added this prop: visible for the component as here:
<ProjectModal visible={this.state.showDialog} />
And here you need to add showDialog to the states in your class with false.
state = {
showDialog: false
};
Now, to step 3.
Third step: Add this part to your code, to be called when you click.
openModal = () => {
this.setState({ showDialog: true });
};
On the other side, you need to implement onClick in the button as follows:
<FABButton onClick={this.openModal.bind(this)}>
<Icon name="add" />
</FABButton>
Fourth step: In the modal/dialog class, you need to store the visible in a new state variable, which is here showDialogModal
constructor(props, context) {
super(props, context);
this.state = {
showDialogModal: this.props.visible
};
}
Now, you need to pass the changed state from the first class to the modal/dialog class, there are more than one option that React gives you, I used this one in fifth step. Fifth step: use this React event componentWillReceiveProps as below.
componentWillReceiveProps(nextProps) {
if (this.props.showDialogModal != nextProps.visible) {
this.setState({
showDialogModal: nextProps.visible
});
}
}
This will reflect any change in visible property from the first class to our new one here which is showDialogModal
Now in the render part, you need to check the docs of your components, here I started with React-Bootstrap.
Sixth step: use the show property in your component.
<Modal show={this.state.showDialogModal} onHide={this.closeModal}>
onHide is for closing the dialog, which makes you need to implement this too.
closeModal = () => {
this.setState({ showDialogModal: false });
};
Finally, in the closing button, add this:
<Button onClick={this.closeModal.bind(this)}>Close</Button>
Good luck.
I am new to react native.
My question is pretty simple: my screen contains 5 buttons. Each one opens the same < Modal > component. I need to dynamically change the content of the modal, depending on the button clicked.
For example:
if I click the first button, a text input will be shown into the modal.
If I click the second button, checkboxes will be shown into the modal.
Here's my modal :
<Modal
visible={this.state.modalVisible}
animationType={'slide'}
onRequestClose={() => this.closeModal()}>
<View style={style.modalContainer}>
<View style={style.innerContainer}>
<Text>This is content inside of modal component</Text>
<Button
onPress={() => this.closeModal()}
title="Close modal"
>
</Button>
</View>
</View>
</Modal>
Here I open it :
openModal() {
this.setState({ modalVisible: true });
}
Here I call the function (on button press) :
onPress={() => this.openModal()}
I've heard about using props/children, but I don't know how to use them is this case.
Can anyone please help ?
Here is quick example to show who to render different content based on input you provide.
Modal Content
renderModalContent(type, data) {
switch(type) {
1: {
return (
<View>{..data}</View>
)
}
2: {
return (
<Button>...</Button>
)
}
default: (<CustomComponent data={data} />)
}
}
Modal
<Modal>
<View>
{this.renderModalContent(this.state.type, this.state.modalContentData)}
</View>
</Modal>
Here you decide which view you want to render and pass its data.
openModal() {
this.setState({ modalVisible: true, type: 1, data: {...} });
}
You should modify your Modal component so that it renders the base layout with space for dynamic content to be rendered. The content will be passed in as children, via Props. This will mean the modal is dynamic and will / should support future requirements. Try to avoid the switch case in the modal render suggestion unless you have very specific requirements that are unlikely to change in the future, or if you want to do things the React way.
Then for each variant of your Modal (TextInput, Checkbox etc.) create a new Component that wraps the Modal component and have each button initiate rendering the specific component.
If you're using Redux then you would be creating containers, connecting to Redux and passing dynamic state variables. You don't have to use Redux but the principle is the same.
Here's a very basic example to illustrate my point.
// Basic modal that renders dynamic content
const Modal = props => {
const { children } = props;
render (
<View style={styles.modal} >
{children}
</View>
);
}
// Specific modal implementation with TextInput
const ModalWithTextInput = props => (
<Modal>
<TextInput
value={props.someValue}
/>
</Modal>
)
// Specific modal implementation with Switch
const ModalWithSwitch = props => (
<Modal>
<Switch
value={props.someValue}
/>
</Modal>
)
Then in your component that launches the modals, do something like this...
class MyComponent extends Component {
openTextModal = () => {
this.setState({ modalType: 'text' });
}
openSwitchModal = () => {
this.setState({ modalType: 'switch' });
}
renderModal = (type) => {
if (type === 'text') {
return(<ModalWithTextInput />)
}
if (type === 'switch') {
return(<ModalWithSwitch />)
}
}
render() {
const { modalType } = this.state;
render (
<View>
<View>
<TouchableWithX onPress={this.openTextModal} />
<TouchableWithX onPress={this.openSwitchModal} />
</View>
<View>
{this.renderModal(modalType)}
</View>
</View>
);
}
}
Please note this code has not tested but the principle is sound.
Is it good to just specify className for element so i could find it later in the DOM through getElementsByClassName for manipulations?
Adding a class to find the DOM element? Sure you can do that, but refs are probably the better solution.
Manipulating the DOM element? That's an absolute no-go. The part of the DOM that is managed by React should not be manipulated my anything else but React itself.
If you come from jQuery background, or something similar, you will have the tendency to manipulate element directly as such:
<div class="notification">You have an error</div>
.notification {
display: none;
color: red;
}
.show {
display: block;
}
handleButtonClick(e) {
$('.notification').addClass('show');
}
In React, you achieve this by declaring what your elements (components) should do in different states of the app.
const Notification = ({ error }) => {
return error
? <div className="notification">You have an error</div>
: null;
}
class Parent extends React.Component {
state = { error: false };
render() {
return (
<div>
<Notification error={this.state.error} />
<button onClick={() => this.setState({ error: true })}>
Click Me
</button>
}
}
The code above isn't tested, but should give you the general idea.
By default, the state of error in Parent is false. In that state, Notification will not render anything. If the button is clicked, error will be true. In that state, Notification will render the div.
Try to think declaratively instead of imperatively.
Hope that helps.
When using React, you should think about how you can use state to control how components render. this.setState performs a rerender, which means you can control how elements are rendered by changing this.state. Here's a small example. I use this.state.show as a boolean to change the opacity of the HTML element.
constructor(props) {
super(props)
this.state = {
show: true
}
}
handleClick() {
this.setState({show: false})
}
render() {
const visibility = this.state.show ? 1 : 0
return (
<button style={{opacity: visibility} onClick={() => this.handleClick()}>
Click to make this button invisible
</button>
)
}