Probably a simple question with a simple answer, but I can't figure this out. Blame the heat. My simulator prints 'this is a test' on the screen, when I want it to say 'changed'. What am I doing wrong?
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Template from './src/components/Template';
export default class App extends React.Component {
constructor(props) {
super(props);
this.foo= "this is a test";
}
changeMe = () => {
this.foo = 'changed';
}
componentDidMount(){
this.changeMe();
}
render() {
return (
<Template foo={this.foo} />
);
}
}
Use state
You are using a class attribute witch will not trigger a re-render when changed. In react a component will re-render when it receives new props or when the state is changed (there's also a way to force it but best not to do that).
Example using the state to trigger a rerender
import React from 'react';
import { StyleSheet, Text, View } from 'react-native';
import Template from './src/components/Template';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state ={foo: "this is a test"};
}
changeMe = () => {
this.setState({foo:'changed'})
}
componentDidMount(){
this.changeMe();
}
render() {
return (
<Template foo={this.state.foo} />
);
}
}
When passing new props to a component it will also re-render (unless you implement componentShouldUpdate).
State explanation
So inside a react component you have a local state object in this.state it can be set in the constructor like this.state = {bar: 'foo'};. After the constructor has set the state it should only be changed with the this.setState() method.
Upon changing the state with setState the component will re-render with the updated values.
The state is not available outside of the component, at least not available as this.state because that would call the local state of the current component.
If you need to use a value from the state of a parent component you can pass it to a child. At that point it becomes a prop of the child accessible with
this.props
To change the value of state from a child component you should pass a function to the child that changes the state of the parent.
This process of passing state changing functions becomes increasingly complex as your app grows, I would suggest using a library like Redux to manage app state via actions and reducers. There is a steep learning curve but once you have a grasp of this modified flux methodology you will wonder how you ever lived without it.
Related
To my knowledge, when some events happen, react creates a virtual-DOM from scratch and compares it to the old virtual-DOM. If that is the case, when the render method is called, react should create components and return them to compare.
import React, { Component, Fragment } from 'react';
import ReactDOM from 'react-dom';
import './index.css';
class Demo extends Component {
constructor(props) {
console.log("constructor called");
super(props);
this.state = { dummy: 1, };
}
render = () => <button onClick={this.props.onChange}> button </button>;
}
class App extends Component {
state = { num: 0, };
onChange = () => {
this.setState({ num: this.state.num+1 }); // mutate state to trigger a render
}
render() {
console.log("rendered");
return (
<Fragment>
<Demo onChange={this.onChange} />
</Fragment>
);
}
}
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
document.getElementById('root')
);
A quick console log reveals that the constructor is called the first time to mount the Demo component onto the page. However, subsequent renders do not create new Demo objects that are compared with the old virtual-DOM (since the constructor is not called).
At first, my theory was that when the constructor of Demo is called, it then calls the super constructor to check if a similar object with the same props already exists. But that was not the case as proven by moving console.log("constructor called"); before calling the parent constructor.
So my question is how does react know not to create another object?
The key here is that Demo is not unmounted. When you first render the app, it renders and mounts the Demo component and passes the onChange prop. But, when the callback is invoked from Demo it sets the state on App. Calling setState in App doesn't unmount the Demo component, so there's no need to mount it again. When the component is mounted initially is when the constructor runs. If you had a toggle on the App component that would only show the component within render if a certain condition is true, that would trigger the component to unmount.
Check out this codesandbox and play around a little: https://codesandbox.io/s/lifecycle-methods-vivn6?file=/src/Lifecycle.js.
Also, this is a cool diagram to get a sense of what's happening: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/
The main key is that the constructor runs when the component mounts. It will only run again if the component is unMounted from the DOM and then remounted later.
I am coding a checkers web app and have set up user interaction by placing onclick functions on the components. I have two different components pieces and spaces. Ideally, the onclick functions are linked to methods in the parent component which interpret the state of the component being clicked and responds by returning data back to the component or a separate component. The issue is, I do not understand how to send data to child components from the parent following the mounting of said child component. I understand you can use props to initialize the state in a child component, but is there anyway I can update the child component from the parent following this initialization? I am new to react so I'm not exactly sure how inter component communication works quite yet.
You can pass update function to child, I hope this helps.
Parent
import React, { Component } from 'react'
export default class Parent extends Component {
constructor(props) {
super(props);
this.state={
something:""
}
}
updateSomething = (anotherThing) => {
this.setState({something:anotherThing})
}
render() {
return (
<div>
{/* we are passing updateSomething function in props*/}
<ChildComponent updateSomething={(anotherThing)=>this.updateSomething(anotherThing)}/>
</div>
)
}
}
Child
import React, { Component } from 'react'
export default class ChildComponent extends Component {
render() {
return (
<div>
{/* we are using updateSomething function to update parents state*/}
<button onClick={()=>this.props.updateSomething("anotherThing")}>Update Parents state</button>
</div>
)
}
}
I think it works differently but I don't know how it works.
1. Using class variable
export default class Test extends Component {
constructor() {
this.active = false;
}
render() {
this.active = this.props.name === 'Dan'? true : false;
return (
<div>
{this.active? 'ssup?' : 'noooo'}
</div>
);
}
}
2. Using React component state
export default class Test extends Component {
constructor() {
this.state = { active: false };
}
render() {
if(this.props.name === 'Dan') {
this.setState({active: true});
}
return (
<div>
{this.active? 'ssup?' : 'noooo'}
</div>
);
}
}
I think it doesn't need to re-render using State if it's only affected by received props.
The difference between the two is that React will re-render your component when state changes (with this.setState(/*...*/)).
If you update the class variable, React will be unaware of it and won't re-render your component.
Note that what you're achieving in your code requires neither state or class variable. You're simply computing another value directly from the props. A better way to write your component would be like this :
export default class Test extends Component {
render() {
const active = this.props.name === 'Dan';
return (
<div>
{active? 'ssup?' : 'noooo'}
</div>
);
}
}
The simple answer to your question is that by using state you call the setState() which automatically calls render() automatically. Which cannot be obtained by class variables
You use the `state variables` when you want to change the component when that variable is changed.
When you don't want to automatically call `render()` you use the `class` variables
React component only re-renders when there are changes to state or class. But updating class variable involves neither so it does not trigger render.
Though using state may seem similar to class variable but state is a protected keyword in React that refers to stored component data. The major difference between using class variables and state is updating data. Instead of manually reassigning the variable, you call this.setState().
When you call this.setState(). It compares this new state to the previous state. If there is a change, React re-renders the component, resulting in the updated count value displayed on the screen.
But when you update class variable, it sure gets updated but does no re-render. You can do so using this.forceUpdate(). But Normally you should try to avoid all uses of forceUpdate() and only read from this.props and this.state in render().
Refer to this article for detailed info.
I have a component that has two children:
A component where the user inputs and select a topic
A component that renders results based on the topic selected in the previous component
So this is the code of the parent component:
import SearchBox from "../../components/SearchBox";
import Results from "../../components/Results";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
selectedTopic: null,
};
}
onSelectTopic = (topic) => this.setState({
selectedTopic: topic,
});
render() {
return (
<div>
<SearchBox
onSelectTopic={this.onSelectTopic}
/>
<Results
key={this.state.selectedTopic && this.state.selectedTopic.id}
selectedTopic={this.state.selectedTopic}
/>
</div>
);
}
}
export default Home;
Basically, the SearchBox component updates the state of the parent component that propagates the selection to the Results component.
The Results component has its own state, that I want to clear when a new selection is passed. Instead of using getDerivedStateFromProps in Results to clear its state I have added a key to <Results key={...}/> as in the snippet above.
I was expecting that every time the state in the parent component updates (ie, the onSelectTopic method is called above), the Results component will be recreated, but instead the component does not update at all. Am I missing something? If I remove its key property, Results updates as expected (ie without clearing its internal state, which is what I want to achieve by using the key)
Btw, I'm using preact instead of react, but my understanding is that the behaviour regarding the key property on components is the same.
I'm rendering a custom modal component that is displayed based on props passed in from a parent component. The prop isVisible is initially false and then updated in the parent component via a button. I'm checking the component state via the console.log statement in the render function. When the component is first initialized, it logs false false as expected but when the isVisible is updated, it returns false true. Why is the state not updating with the props?
class MyModal extends React.Component {
constructor(props) {
super(props);
this.state = {
createModalVisible:props.isVisible,
};
setCreateModalVisible = (visible) => {
this.setState({createModalVisible:visible});
}
componentWillMount(){
this.setCreateModalVisible(this.props.isVisible);
}
}
render() {
console.log(this.state.createModalVisible,this.props.isVisible);
return (//modal stuff)
}
}
export default MyModal
I get that this is probably pretty basic component lifecycle stuff but I couldn't figure it out from the docs and am fairly new to React.
Since the constructor and componentWillMount is only run once per component mounted in to the DOM tree the state wouldn't be updated on each props being passed down.
this.state is local for each component instance and needs to be updated with this.setState() to be able to use an updated state object on each render pass.
Use
componentWillReceiveProps in which you can set the state to reflect the new prop being passed in for you to get what you need.