React components make inheritance through Compositon - reactjs

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.

Related

State doesn't update when props changes

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 />;
}
}

Passing an object as props and receiving it

I have a component Data and its child component BarChart.
Data component looks as following:
export default class Data extends Component {
constructor(props) {
super(props);
this.state = {
data: {
labels: [],
datasets: [{
label: "",
data: [],
backgroundColor: ''
}]
}
}
}
componentWillMount() {
this.geData();
}
geData = () => {
let labelsData = someContent;
let datasets = otherContentl;
this.setState({data: {...this.state.data, labels: labelsData, datasets: datasets}}, ()=>{console.log(this.state.data)});
}
render(){
return (
<BarChart data={this.state.data} />
);
}
}
When I check the result of console.log(this.state.data) in getData function, it prints out the correct data.
However, when I receive the props in BarChart component, I only receive datasets key filled with the correct data, but labels key is an empty array.
export default class BarChart extends Component {
constructor(props) {
super(props);
console.log(props);
}
componentWillMount() {
this.setState({chartData: this.props.data});
}
render() {
return (
<div className="barChart">
<Bar
data={this.state.chartData}
/>
</div>
);
}
}
Why does that happen? How can it be fixed?
What I had in BarChart component is:
constructor(props) {
super(props);
this.state = {
chartData: {}
}
}
componentWillMount() {
this.setState({chartData: this.props.data});
}
render() {
return (
<div className="barChart">
<Bar
data={this.state.chartData}
/>
</div>
);
}
What I changed is receiving the props immediately and using it, instead of receiving it in state or componentWillMount:
export default class BarChart extends Component {
constructor(props) {
super(props);
this.state = {}
}
componentWillMount() { }
componentDidMount() { }
render() {
return (
<div className="barChart">
<Bar
data={this.props.data}
/>
</div>
)
}
}
So, whenever the props is changed, the component will re-render.

How to setProps in ReactJS

I have 2 Components one called NodeWidget and another called PopupWidget. In the NodeWidget it has a Model assigned to it which looks like the following:
PopupModel
export class PopupModel {
question: string;
model: string;
constructor(question: string, model: string) {
this.question = question;
this.model = model;
}
}
The parent Component is NodeWidget which passes in the Model to the PopupWidget with data in.
NodeWidget
{ this.state.showComponent ?
<PopupWidget model={this.props.popupModel} /> :
null
}
Then finally in the child Component we have this code:
export interface PopupWidgetProps {
model: PopupModel;
}
export interface PopupWidgetState { }
export class PopupWidget extends React.Component<PopupWidgetProps, PopupWidgetState> {
constructor(props: PopupWidgetProps) {
super(props);
this.state = { };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
console.log(this.props);
}
render() {
return (
<div className="popup">
<div className="popup_inner">
<h1>TEST</h1>
<input type="text" value={this.props.model.question} placeholder="Write a question..." />
<button onClick={this.handleClick}>close me</button>
</div>
</div>
);
}
}
I want to be able to bind the value of the input to the model and then for it to update the original model in the Parent Component, am i doing this correctly as it does not seem to work.
You can do this to pass the input result to parent component on the button click:
PopupWidget :
export class PopupWidget extends React.Component<PopupWidgetProps, PopupWidgetState> {
constructor(props: PopupWidgetProps) {
super(props);
this.state = { question: '' };
this.handleClick = this.handleClick.bind(this);
}
handleClick() {
this.props.inputResult(this.state.question)
}
render() {
return (
<div className="popup">
<div className="popup_inner">
<h1>TEST</h1>
<input type="text" value={this.state.question} onChange={(question) => { this.setState({ question })}} placeholder="Write a question..." />
<button onClick={this.handleClick}>close me</button>
</div>
</div>
);
}
}
NodeWidget :
constructor(props) {
super(props);
this.getInputResult = this.getInputResult.bind(this);
}
getInputResult(question) {
this.props.inputResult(question);
this.setState({ showComponent: false });
}
...
{ this.state.showComponent ?
<PopupWidget inputResult={this.getInputResult} /> :
null
}
Finally in PopupModel (i assume this is a react component, i don't know if you can work with simple es6 class in react):
export class PopupModel extends React.Component {
constructor(props) {
this.state = { question: '', model: '' }; // set your initial state
this.getInputResult = this.getInputResult.bind(this);
}
getInputResult(question) {
this.setState({ question }); // here's our result from the input
}
render(){
return(<NodeWidget inputResult={this.getInputResult} />);
}
}
This can be pretty boring to handle if you have multiple components between the two which have to communicate.
You can use a HOC like Redux or MobX to handle an app state that can be passed in any component, and any component can dispatch actions to update the app state, you should go for it if you have multiple cases like this.

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

blinking text with React using state and setTimeOut

I am trying to show blinking text using react:
class BlinkLable extends React.Component {
constructor(props) {
super(props);
this.state = {showlabel: true,
label: this.props.label
};
this.myFunction = this.myFunction.bind(this);
}
myFunction()
{
var sLb = ! (this.state.showlabel);
this.setState({showlabel: sLb});
}
componentDidMount() {
setTimeout(this.myFunction, 3000)
}
render() {
return (
<div>
{(this.state.showlabel)?this.state.label:''}
</div>
);
}
}
ReactDOM.render(
<BlinkLable label='MY MESSAGE'/>,
document.getElementById('labelId')
);
However, after it shows MY MESSAGE, this message dissapears and never comes back. What could be the problem?
You need to use setInterval() method.
componentDidMount() {
setInterval(this.myFunction, 3000)
}
More Info: 'setInterval' vs 'setTimeout'
Modify this function to:
myFunction()
{
this.setState((prevState) => {
return {
showlabel: !prevState.showLabel
}
});
}
class BlinkLable extends React.Component {
constructor(props) {
super(props);
this.state = {showlabel: true,
label: this.props.label
};
this.myFunction = this.myFunction.bind(this);
}
myFunction(){
var sLb = ! (this.state.showlabel);
this.setState({showlabel: sLb});
}
render(){
return (
<div>
{(this.state.showlabel)?this.state.label:''}
</div>
);
}
componentDidUpdate() {
setTimeout(this.myFunction, 2000)
}
componentDidMount(){
setTimeout(this.myFunction, 2000)
}
}
ReactDOM.render(
<BlinkLable label='MY MESSAGE'/>,
document.getElementById('labelId')
);
<div id="labelId"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
you need to know react's component life cycle.
componentDidMount() is called only when a component is mounted.
componentDidMount() is invoked immediately after a component is
mounted. Initialization that requires DOM nodes should go here.
Therefore if you want to keep blinking, add componentDidUpdate().

Resources