State doesn't update when props changes - reactjs

i was learning react from 'React docs' for few days and today i got into the trouble. Link to docs: https://reactjs.org/docs/conditional-rendering.html#element-variables.
Exercise is that when the button clicks text will change. In docs, they did it with functions and it works perfectly but i tried it with classes.
The problem is that state doesn't update when props changes, it only have its initial value. I'm struggling with it since 2 hours and didn't find the solution. I'm new to React so please be forbearance.
Code:
class UserGreeting extends React.Component {
render() {
return (
<h1>Welcome back!</h1>
);
}
}
class GuestGreeting extends React.Component {
render() {
return (
<h1>Please sign up!</h1>
);
}
}
class Greeting extends React.Component {
constructor(props) {
super(props);
this.state = {isLoggedIn: this.props.isLoggedIn};
}
render() {
let isLoggedIn = this.state.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
}
class LoginButton extends React.Component {
constructor(props) {
super(props);
this.state = {onClick: this.props.onClick};
}
render() {
return (
<button onClick={this.state.onClick}>Login</button>
);
}
}
class LogoutButton extends React.Component {
constructor(props) {
super(props);
this.state = {onClick: this.props.onClick};
}
render() {
return (
<button onClick={this.state.onClick}>Logout</button>
);
}
}
class LoginControl extends React.Component {
constructor(props) {
super(props);
this.state = {isLoggedIn: true};
}
handleLoginClick = () => {
this.setState({isLoggedIn: true});
}
handleLogoutClick = () => {
this.setState({isLoggedIn: false});
}
render() {
const isLoggedIn = this.state.isLoggedIn;
let button;
if (isLoggedIn) {
button = <LogoutButton onClick={this.handleLogoutClick} />;
} else {
button = <LoginButton onClick={this.handleLoginClick} />;
}
return (
<div>
<Greeting isLoggedIn={isLoggedIn} />
{button}
</div>
);
}
}
ReactDOM.render(
<LoginControl />,
document.getElementById('root')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>

You are storing your answer in the state in the Greeting component inside the constructor. The constructor will only be called once at mount component.
Change your Greeting component with below code**
class Greeting extends React.Component {
render() {
let isLoggedIn = this.props.isLoggedIn;
if (isLoggedIn) {
return <UserGreeting />;
}
return <GuestGreeting />;
}
}

Related

Call child component function from parent

How do I call a child component function from the parent component? I've tried using refs but I can't get it to work. I get errors like, Cannot read property 'handleFilterByClass' of undefined.
Path: Parent Component
export default class StudentPage extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
newStudentUserCreated() {
console.log('newStudentUserCreated1');
this.refs.studentTable.handleTableUpdate();
}
render() {
return (
<div>
<StudentTable
studentUserProfiles={this.props.studentUserProfiles}
ref={this.studentTable}
/>
</div>
);
}
}
Path: StudentTable
export default class StudentTable extends React.Component {
constructor(props) {
super(props);
this.state = {
studentUserProfiles: props.studentUserProfiles,
};
this.handleTableUpdate = this.handleTableUpdate.bind(this);
}
handleTableUpdate = () => (event) => {
// Do stuff
}
render() {
return (
<div>
// stuff
</div>
);
}
}
UPDATE
Path StudentContainer
export default StudentContainer = withTracker(() => {
const addStudentContainerHandle = Meteor.subscribe('companyAdmin.addStudentContainer.userProfiles');
const loadingaddStudentContainerHandle = !addStudentContainerHandle.ready();
const studentUserProfiles = UserProfiles.find({ student: { $exists: true } }, { sort: { lastName: 1, firstName: 1 } }).fetch();
const studentUserProfilesExist = !loadingaddStudentContainerHandle && !!studentUserProfiles;
return {
studentUserProfiles: studentUserProfilesExist ? studentUserProfiles : [],
};
})(StudentPage);
My design here is: component (Child 1) creates a new studentProfile. Parent component is notified ... which then tells component (Child 2) to run a function to update the state of the table data.
I'm paraphrasing the OP's comment here but it seems the basic idea is for a child component to update a sibling child.
One solution is to use refs.
In this solution we have the Parent pass a function to ChildOne via props. When ChildOne calls this function the Parent then via a ref calls ChildTwo's updateTable function.
Docs: https://reactjs.org/docs/refs-and-the-dom.html
Demo (open console to view result): https://codesandbox.io/s/9102103xjo
class Parent extends React.Component {
constructor(props) {
super(props);
this.childTwo = React.createRef();
}
newUserCreated = () => {
this.childTwo.current.updateTable();
};
render() {
return (
<div className="App">
<ChildOne newUserCreated={this.newUserCreated} />
<ChildTwo ref={this.childTwo} />
</div>
);
}
}
class ChildOne extends React.Component {
handleSubmit = () => {
this.props.newUserCreated();
};
render() {
return <button onClick={this.handleSubmit}>Submit</button>;
}
}
class ChildTwo extends React.Component {
updateTable() {
console.log("Update Table");
}
render() {
return <div />;
}
}

React properties not bubbling down

I have a react component(parent) that has as state another react component(child)
The parent passes down is't state as props to the child.
But if I do setState on the passed down property, it does not update in the child.How do I make such that a change in state is reflected in the child?
See code:
class Child extends React.Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
{this.props.x}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {x: 1, intervalID: 0, currentScreen: <Child x={0} />}
}
componentDidMount() {
let self = this
let intervalID = setInterval(function() {
self.setState({x: self.state.x+1})
}, 1000)
self.setState({intervalID: intervalID, currentScreen: <Child x={self.state.x} />})
}
render() {
return (
<div>
{this.state.currentScreen}
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('app'))
Below code is working.
import React from 'react';
import ReactDOM from 'react-dom';
class Child extends React.Component {
render() {
return (
<div>
{this.props.x}
</div>
)
}
}
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = {x: 1, intervalID: 0, currentScreen: <Child x={0} />}
}
componentDidMount() {
let intervalID = setInterval(() => {
const x = this.state.x + 1;
this.setState({
x: x,
currentScreen: <Child x={x} />
});
}, 1000)
this.setState({intervalID: intervalID, currentScreen: <Child x={this.state.x} />})
}
render() {
return (
<div>
{this.state.currentScreen}
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'))
Your child component is not updating because for the lifecycle of parent component componentDidMount is only called once when it is being mounted.
If you need to update your state on regular interval you can do something like :
import React, { Component } from 'react';
import { render } from 'react-dom';
class Child extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
{this.props.x}
</div>
)
}
}
class Parent extends Component {
constructor(props) {
super(props)
this.state = { x: 1, intervalID: 0 }
}
componentDidMount() {
let self = this
let intervalID = setInterval(function () {
self.setState({ x: self.state.x + 1 })
}, 1000)
self.setState({ intervalID: intervalID })
}
render() {
return (
<div>
<Child x={this.state.x}/>
</div>
)
}
}
render(<Parent />, document.getElementById('root'))
for your case, just so when setState is done, it will call render again and it will pass the latest value of x to the child component.
You can check out live working example on stackblitz
It is a bad practise to maintain JSX in the state. Move all your JSX into the render() and use state variables to manage the state as shown below (For brevity only the Parent component code is shown).
Further instead of doing let self=this use the arrow function for clarity.
Note that you need to use the updater function when setting the state if your new state depends on the previous state. This is because React does batch updates for state. More information can be found in the official documentation.
class Parent extends React.Component {
constructor(props) {
super(props)
this.state = { x: 1 }
}
componentDidMount() {
setInterval(() => {
this.setState((prevState, props) => {
return { x: prevState.x + 1 };
});
},3000)
}
render() {
return (
<div>
<Child x={this.state.x} />
</div>
)
}
}
ReactDOM.render(<Parent />, document.getElementById('root'))
Above function will update the value of x every 3 seconds. Below is a working example
https://codesandbox.io/s/2677zoo4p

Not able to render state using componentWillReceiveProps

I am getting props from parent component and trying to render
From parent component, I am passing the headings
Parent Component:
class CoreCloudServices extends React.Component{
constructor(props){
super(props)
this.state = {
services:[]
}
}
loadData(){
var url = "https://api.myjson.com/bins/1ftfdx";
fetch(url)
.then(response => {
return response.json();
})
.then(d => {
this.setState({ services: d });
})
.catch(error => console.log(error))
}
componentDidMount() {
this.loadData();
}
render(){
<StatusFrame headings={this.state.services}/>
}
Child Component:
class StatusFrame extends React.Component{
constructor(props){
super(props)
this.state = {
labelHeading : this.props.headings
}
}
componentWillReceiveProps(newProps)
{
this.setState({labelHeading: newProps.headings} , ()=>{
console.log(this.state.labelHeading);
});
}
render(){
return(
<div>
<div>
{
this.state.labelHeading.map(((head, index) => {
<div>child {head.title}</div>
})
)
}
</div>
</div>
)}}
this.state.labelHeading is null but I am setting the state in componentwillreceiveprops()
you can just use the props without using the state , and you must return from your parent render method , also in map callback you should return too
class CoreCloudServices extends React.Component{
//...
render(){
return (<StatusFrame headings={this.state.services}/>)
}
}
class StatusFrame extends React.Component {
constructor(props){
super(props)
}
render() {
return (
<div>
<div>
{
this.props.headings !== null ?
this.props.headings.map(( (head, index) =>
{
return <div>child {head.title}</div>
}))
:
null
}
</div>
</div>
)
}
}

Unable to find an error

i know it's a minor and maybe a fool question but i'm stuck for about an hour at an error which i cant see. This is my code:
const ModalRoot = ({ modalType, modalProps, locale }) => {
if (!modalType) {
return <span />;
}
return (
<IntlProvider
locale={locale}
key={locale}
messages={messagesFor(locale)}
>
<div className="backdrop">
{renderAppropriateModal(modalType, modalProps)}
</div>
</IntlProvider>
);
};
The console shows an error in the if saying unexpected token. Why is this happening??
This can be your error...
check this samples.
Wrong place to declare.
import React from 'react';
class YOURCLASS extends React.Component {
constructor(props) {
super(props);
}
//do not place this ModalRoot here
const ModalRoot = ({ modalType, modalProps, locale }) => {
//contents
}
render(){
return(
<div>{yourContent}</div>
);
}
}
Right place to declare
import React from 'react';
//Place it here outside the class YOURCLASS
const ModalRoot = ({ modalType, modalProps, locale }) => {
//contents
}
class YOURCLASS extends React.Component {
constructor(props) {
super(props);
}
render(){
return(
<div>{yourContent}</div>
);
}
}
if you still intends to do it inside class... better use function instead...
import React from 'react';
class YOURCLASS extends React.Component {
constructor(props) {
super(props);
}
//function type ModalRoot
ModalRoot(modalType, modalProps, locale){
//contents
return <IntlProvider />;
}
render(){
const {modalType, modalProps, locale} = this.props;
let yourContent = this.ModalRoot(modalType, modalProps, locale);
return(
<div>{yourContent}</div>
);
}
}
hope it helps...

React components make inheritance through Compositon

i dont understand how to make inheritance make through Compositon
for example we have a simple Clock component:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {date: new Date()};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {this.state.date.toLocaleTimeString()}.</h2>
</div>
);
}
}
and then we want to make another Clock Component HappyClock and it have same logic but different render method:
class HappyClock extends Clock {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>Hello, Happy world!</h1>
<h2>It is Happy {this.state.date.toLocaleTimeString()} Time.</h2>
</div>
);
}
}
how i can do it through composition if we want to have many different Clocks and same logic
You can create pure components which renders what you already have in your render methods.
For example:
class ClockDisplay extends React.Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<h1>{this.props.greeting}</h1>
<h2>{this.props.displayText}</h2>
</div>
);
}
}
Then you can use this component in the Clock component.

Resources