React change class attributes - reactjs

I'm trying to change class attribute in a function, I have a button and I want to change the text as soon as the user clicks it.
I wrote a class and everything but when the user clicks the button nothing happens!
I console logged to see if the variables actually changed and they did so why does it happen?
Here is my code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
buttonStatus: 'Post room',
value: '',
}
}
PostANewRoomScreen = () => {
let {data} = '';
return (
<View style={{padding: 15}}>
<TouchableOpacity
style={styles.button}
disabled={this.state.loading}
onPress={() => this.postroom(data)}
>
<Text style={{color: "white" , fontSize: hp('2.5%'), padding: 5}}>
{this.state.loading && <Animated.Image style={{height: hp('3%'), width: wp('2%')}} source={{uri: 'https://media0.giphy.com/media/JTVkOqJ1RyYEBnyoRb/giphy.gif'}} />}
{this.state.buttonStatus}
</Text>
</TouchableOpacity>
</View>
postroom = (value) => {
this.state.loading = true;
this.state.buttonStatus = "button clicked!";
}

this.state.loading = true;
this.state.buttonStatus = "button clicked!";
This does not re render component. You have to use useState Hook (in funcation component) or setState() (in class component) in order to update the state. I suggest converting component to functional component and use hooks to manage state.
const [buttonStatus, setButtonStatus] = useState('Post Room');
then use setButtonStatus(value) inside your postroom function.
Please refer react doc for useSate Hooks
Further, Your existing code has some issues. you are trying to access and mutate state of App component. if you need to share the component state try to lift the state up. If you need to pass properties to component use props.

Related

How to Change Title Attribute in React Native Button Component

I have a few React components that look something like this within an app:
<View style={{margin: 5}}>
<Button title='Enable Headlights' color='#00759A' onClick={doChanges}/>
</View>
I want the Button component to change its color and text whenever its clicked. I was planning on having the function doChanges do this, but I can't figure out a way to modify the title and color attributes. I've seen custom made components that make these kinds of changes in state by calling setState within the component, but Button isn't a component that I made. I tried doing something along the lines of:
var text = "Enable headlights"
function doChanges(text) {
text = "Disable headlights"
}
<View style={{margin: 5}}>
<Button title={text} color='#00759A' onClick={()=> {doChanges(text)}}/>
</View>
But that doesn't seem to work the way I want it to. I think it's because even though the variable text is changing, the actual component isn't rerendering, so the changes in text never actually appear.
There doesn't seem to be a way to force the component to rerender, and I don't see how there's a way I can modify the title and color attributes of the Button component.
Do I have to make a custom component that's subclassed from Button in order to get the functionality I want? Or is there a specific way for how I'm supposed to go about doing something like this in React?
Any help would be appreciated!
If you are supposed to make changes at the UI level then you should use the state to render the updated view.
like:
If you are using Class Component:
class Demo extends Component {
constructor(props) {
super(props)
this.state = {
buttonText: "Enable headlights"
}
this.doChanges = this.doChanges.bind(this);
}
function doChanges(text) {
this.setState({ buttonText: "Disable headlights" });
}
render() {
return (
<View style={{margin: 5}}>
<Button title={this.state.} color='#00759A' onClick={()=> {doChanges(text)}}/>
</View>
)
}
}
If you are using Functional Component:
const Demo = props => {
const [buttonText, setButtonText] = useState("Enable headlights")
function doChanges(text) {
setButtonText("Disable headlights");
}
return (
<View style={{margin: 5}}>
<Button title={text} color='#00759A' onClick={()=> {doChanges(text)}}/>
</View>
)
}
check out more about the state:
https://reactjs.org/docs/state-and-lifecycle.html

TouchableOpacity onPress not working(emulator test)

So just getting started with react-native. I have a stateful component built as below :
class LoginView extends Component {
constructor(props) {
super(props);
this.state = {
inProgress: false
};
}
processLogin(){
this.setState({
inProgress: true
})
}
render() {
return (
<View style={styles.mainContainer}>
<View style={styles.loginContainer}>
<TextInput style={styles.input} placeholder="Password" />
<TouchableOpacity style={styles.button} onPress={() => this.processLogin()}>
{!this.state.inProgress && <Text style={styles.button_text}>Log in</Text>}/>}
</TouchableOpacity>
</View>
</View>
);}}
As you can see, i am binding the onPress event of the touchable opacity to the function processLogin. But for some reason, the function isn't fired!
The state doesn't change, also tried console.log but didn't fire either. Made sure that TouchableOpacity is imported from React-Native.
I am guessing that i messed up the binding. Any leads would be appreciated!
Note: Have only been trying this in the emulator, not in a real device yet.
instead of using the arrow function on onPress prop, use it when you are declaring the business function processLogin
So concretly, replace the declaration of this method by :
processLogin = () => {
this.setState({
inProgress : true
})
}
Then on onPress method, just change make it like this :
onPress = {this.processLogin}
Contrarily, you refer to the wrong this
Hope it's gonna help you sir
Regards.

FlatList calling twice

I have this code
class Home extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: []
}
this._handleRenderItem = this._handleRenderItem.bind(this);
this._keyExtractor = this._keyExtractor.bind(this);
}
componentDidMount() {
let success = (response) => {
this.setState({ dataSource: response.data });
};
let error = (err) => {
console.log(err.response);
};
listarProdutos(success, error);
}
_keyExtractor = (item, index) => item._id;
_handleRenderItem = (produto) => {
return (
<ItemAtualizado item={produto.item} />
);
}
render() {
return (
<Container style={styles.container}>
<Content>
<Card>
<CardItem style={{ flexDirection: 'column' }}>
<Text style={{ color: '#323232' }}>Produtos atualizados recentemente</Text>
<View style={{ width: '100%' }}>
<FlatList
showsVerticalScrollIndicator={false}
data={this.state.dataSource}
keyExtractor={this._keyExtractor}
renderItem={this._handleRenderItem}
/>
</View>
</CardItem>
</Card>
</Content>
</Container>
);
}
}
export default Home;
The function _handleRenderItem() is being called twice and I can't find the reason. The first time the values inside my <ItemAtualizado /> are empty, but the second was an object.
This is normal RN behavior. At first, when the component is created you have an empty DataSource ([]) so the FlatList is rendered with that.
After that, componentDidMount triggers and loads the updated data, which updates the DataSource.
Then, you update the state with the setState which triggers a re render to update the FlatList.
All normal here. If you want to try, load the datasource in the constructor and remove the loading in the componentDidMount. It should only trigger once.
If you want to control render actions, you can use shouldComponentUpdate method.
For example:
shouldComponentUpdate(nextProps, nextState){
if(this.state.friends.length === nextState.friends.lenght)
return false;
}
it will break render if friends count not change.
I recreated the issue in this snack. https://snack.expo.io/B1KoX-EUN - I confirmed you can use shouldComponentUpdate(nextProps, nextState) to diff this.state or this.props and return true/false - https://reactjs.org/docs/react-component.html#shouldcomponentupdate docs say this callback should be used only for optimization which is what we're doing here.

React: Warning setState Cannot update during an existing state transition (such as within a render)

Error Message:
React: Warning setState Cannot update during an existing state
transition (such as within a render)
This is a react-native app, but I figure, this is more of a react question.
I'm getting the error described in the heading. But I'm puzzled about the reason and I'm not setting state in any of the parent or child components. So I have a grandchild component(CardLayoutResult), which renders a listView and each row renders a new component(Render row) which has a click event, upon clicking it calls a function(onselectLayout) from props(passed from the parent component(Personalization component)(parent function setCardTemplate). This parent function then calls setState of the parent internal state. Then re-rendering occurs.
Why am I getting this error upon click?
Parent Component
export class Personalization extends Component {
constructor(props) {
super(props);
this.state = {
showModel: true,
editorState: {}
};
this.setCardTemplate = this.setCardTemplate.bind(this);
}
setCardTemplate(selectedTemplateObj){
console.log(JSON.stringify(selectedTemplateObj));
this.setState({
showModel: false,
editorState: selectedTemplateObj
});
}
render() {
return (
<View>
<CreateCardStep containerState={{showModel:this.state.showModel ,
editorState: this.state.editorState
setCardTemplate:this.setCardTemplate}} />
</View>
)
}
Child Component
const CreateCardStep = (_props) => {
return (
<View>
<CardLayoutResults containerState={_props.containerState} />
</View>
);
}
Grand Child Component
export class CardLayoutResults extends Component {
constructor(props) {
super(props);
}
render() {
return(
<View>
<ListView
RenderRow = {(data) => <RenderRow styles={styles} rowObj={data} onSelectLayout={this.props.containerState.setCardTemplate} /> }
</View>
)
}
Render Row
const RenderRow = (props) => {
let base64Image = 'data:image/png;base64,'.concat(props.rowObj.base);
return (
<View style={props.styles.templateImage}>
<TouchableHighlight onPress={() => props.onSelectLayout(props.rowObj)}>
<Image style={props.styles.thumbnail} resizeMode= {Image.resizeMode.contain} source={{uri: base64Image}}/>
</TouchableHighlight>
</View>
);
};
- Update
This issue seems to be when setting state in the setCardTemplate function
You either need to bind your setCardTemplate function in the constructor or call it from a lambda function (binding is better)
<View>
<ListView renderRow = {(data => rowObj={data}} onSelectLayout={() => this.props.containerState.setCardTemplate} />
</View>
I guess the renderRow for listView is not called properly
Try
<View>
<ListView
renderRow = {(data) => <RenderRow rowObj={data} onSelectLayout={this.props.containerState.setCardTemplate} />}
/>
</View>
and pass down the props from personalization component like
return (
<View>
<CreateCardStep containerState={{showModel:this.state.showModel ,
editorState: this.state.editorState
setCardTemplate:(val) => this.setCardTemplate(val)}} />
</View>
)
The problem is with the way you are passing props to the child component from the parent.
In this snippet
<CreateCardStep containerState={{showModel:this.state.showModel ,
editorState: this.state.editorState
setCardTemplate:(val) => this.setCardTemplate(val)}} />
while passing the function as a prop itself you are calling the function so every time the component render the state will be set which is fine.
But when you click the Render Row component state is being set yet for the same property before finishing the render cycle which causes this warning.
To fix it just pass the function from parent as setCardTemplate: this.setCardTemplate. It will set the state (and internally will call the render) only on row press, not every time it is rendered

props data is not working in componentDidMount using react-native

I have been struggling for the last 24 hrs about this one.
The problem is that, I am passing props in my parent class and calling the props data in my child component. When I check in render method the data shows correctly, but when calling it in componentDidMount it shows null.
Here is the sample of my code
class parent extends React.Component{
constructor(props){
super(props);
this.state = {
isOpen: false,
day:'monday',
data:[],
data1:[]
}
}
back () {
this.props.navigator.pop();
}
Mon(){
var record=[];
this.state.data.map((data) => {
if(data.frequency[0].day == 'Monday'){
record.push(data);
}
});
this.setState({data1:record});
}
componentWillMount() {
this.fetchData();
}
fetchData() {
//here i am getting the data from service and set the data to the state
}
render(){
return(
<View style={styles.Maincontainer}>
<View style={styles.navbar}>
<View style={styles.navbarContainer}>
<View style={styles.navbarIcon}>
<TouchableOpacity style={styles.button} onPress={() => this.back()}>
<Image
style={styles.menu2}
source={require('image!nav_arrow')} />
<Text style={styles.navbuttonText}>Back</Text>
</TouchableOpacity>
</View>
<View style={styles.navbarText}>
<Text style={styles.title}>Title</Text>
</View>
<View style={styles.navbarButton}>
</View>
</View>
</View>
<View style={styles.subMainContainer}>
<View style={styles.weekHeader}>
<View style={styles.weekRow}>
<Text style={styles.text} onPress={this.Mon.bind(this)}>Mon</Text>
</View>
</View>
<View style={styles.listBody}>
{this.state.data1.length == 0 ?(
<List data={this.state.data} />
) : (
<List data={this.state.data1}/>
)}
</View>
</View>
</View>
);
}
}
In my child component, I want to display the data in a ListView which is taken from the parent component
class List extends React.Component{
constructor(props){
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
}),
isLoading: true
};
}
componentDidMount(){ //**here props data shows empty**
alert("didMount:"+JSON.stringify(this.props.data));
var data=this.props.data
this.setState({
dataSource: this.state.dataSource.cloneWithRows(data),
isLoading: false
});
}
render() { //**In this alert it shows correct data**
alert("data:"+JSON.stringify(this.props.data));
if (this.state.isLoading) {
return this.renderLoadingView();
}
return(
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderSchedule.bind(this)}
automaticallyAdjustContentInsets={false}/>
);
}
renderLoadingView(){
return(
<View style={styles.loading}>
<ActivityIndicatorIOS
size='large'/>
<Text>
Loading Schedule...
</Text>
</View>
);
}
renderSchedule(data){
return(
<View style={styles.ListContainer}>
<View style={styles.subContainer}>
<Text style={styles.classText}>Class Name: {data.name}</Text>
{data.frequency[0].day == null ? (
<Text style={styles.Day}>Day: {data.frequency[0].date}</Text>
):
<Text style={styles.Day}>Day: {data.frequency[0].day}</Text>
}
</View>
<View style={styles.subContainer1}>
<Text style={styles.startTime}>Start: {data.frequency[0].from}</Text>
<Text style={styles.endTime}>End: {data.frequency[0].to}</Text>
</View>
</View>
)
}
}
Here what i want is that initially i need to display the total data,when click on the text parent class the data need to be changed.I want to show the data in ListView.I already explain above about my problem,So can any one give me suggestions that how to solve my problem, Any help much appreciated
My guess would be that the alert in componentDidMount() is empty because it only called once, at a time when data is still empty, while render() is called on each change of state or props.
I would expect the flow of alerts to look something like this:
Alert called from child's render: "data: (empty)"
Alert called from child's didMount: "didMount:(empty)"
User clicks something in parent, state change, re-render triggered
Alert called from child's render: "data: FOO-content"
React does NOT call componentDidMount() on the second pass. If you want your datasource to be populated on EACH update of the child, you will need to add a componentDidUpdate() method to your child, with copy of the code you have inside componentDidMount().
As a general note: I would advise you to read React's explanation of component lifecycle and methods. I have a feeling that the preparation you are doing inside componentDidMount() is probably better placed elsewhere:
creating a DataSource and initial fill with data from props: in getInitialState()
updating Datasource with data from new props: in componentWillReceiveProps()
I found in React generally that if I want to prepare the initial state of my component based on this.props then I should do it in componentDidUpdate instead of componentDidMount. Components aren't mounted with populated props from the parent component, only updated with them.
I noticed this when doing normal React and doing console.log(this.props) in the render method of my child component. It would log initally as empty, then log again populated with the props. If I called a this.setState in componentDidMount then the population of this.props occurs in the third render. This is because calling this.setState in componentDidMount triggered a pre-update render.
I hope this makes sense and applies to your situation - I arrived at your question looking for a solution to similar behaviour.

Resources