A Component changing its props? - reactjs

So, I want my component to render only when I do it (eg. when it is clicked.) . I don't want it to re-render when its props changes.
So, my idea was to have the parent pass a wasClicked='true' prop, when it is clicked, which I change to 'false' once it is rendered and have a condition in the render body to only render when wasClicked is true.
How is this possible?

With class components, you can decide if you want to update component with a lifecycle method called shouldComponentUpdate. With a functional component, you can decide if the component should update with React.memo

That should be easy, lets say this is a simple component that only renders when wasClicked is true:
simpleComponent.js:
import react from "react";
const SimpleComponent = (props) => {
return <p> some text </p>;
};
export default SimpleComponent;
App.js;
import react from "react";
import SimpleComponent from "./simpleComponent.js";
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
wasClicked: false, //this is the state for wasClicked
};
}
//this is the function that changes wasClicked to true when the component is clicked
handleClick = () => {
this.setState({ wasClicked: true });
};
render() {
return (
<>
{this.state.wasClicked && ( //this will only render SimpleComponent when wasClicked is true
<SimpleComponent
onClick={this.handleClick}
wasClicked={this.state.wasClicked}
/>
)}
</>
);
}
}
export default App;
Hope this was helpful and clear, thanks

Related

How can i refresh my child componet by using a button click on parent componet?

I am new to this stackoverflow world. Please help me here. How can i refresh the child component based on the button click from the parent component. For example, i have a button in parent component called refresh and not passing any state variables or props to child component. but i want the child component to loaded again based on the button click of parent. I tried adding a state variable, but it was not updating the child component.
parent component:
import React from "react";
import ChildComponentForRefreshTesting from "./ChildComponentForRefreshTesting";
class MyComponent extends React.Component {
constructor(props) {
super(props);
this.state = { isRefreshClicked : false };
}
submit() {
this.setState({ isRefreshClicked: true }, () => this.setState({isRefreshClicked: false}));
}
render() {
return (
<div>
<h1>Parent Component ------<button onClick={this.submit}>Refresh</button></h1>
<ChildComponentForRefreshTesting />
</div>
)
}
}
export default MyComponent;
child component:
import React from 'react'
function ChildComponentForRefreshTesting() {
return (
<div>
child component
<br />
</div>
)
}
export default ChildComponentForRefreshTesting;
Changing state should re-render the current component and all the child components inside it.
I ran your code and there is a mistake with how you defined the setState method. You need to use arrow function, otherwise this will be undefined.
So change your submit button like following and it should work.
submit = () => {
this.setState({ isRefreshClicked: true });
};
Here's the stackblitz: https://stackblitz.com/edit/react-f5q3n8?file=src/MyComponent.js
In class components, you can call this.forceUpdate() method to force a rerender.
https://reactjs.org/docs/react-component.html#forceupdate

React - What is meant by 'Do not use HOC’s in the render method of a component. Access the HOC outside the component definition.'?

I am learning HOCs and keep reading the above quote, but I do not understand what it means. If my HOC adds a method to my consuming component, can I use that method in the render method like so? If not how would I do what I am trying to do here:
import React, { Component } from 'react';
import { withMyHOC } from '../with_my_component'
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default withMyHOC(MyComponent );
When you say, do not use HOC within the render method, it means that you shouldn't create an instance of the component wrapped by HOC within the render method of another component. For example, if you have a App Component which uses MyComponent, it shouldn't be like below
import React, { Component } from 'react';
class MyComponent extends Component {
constructor(props) {
super(props);
}
render() {
const { methodFromHOC }= this.props;
const result = methodFromHOC(someArgument);
return (
<div >
{result}
</div>
)
}
}
export default MyComponent;
import { withMyHOC } from '../with_my_component'
export default class App extends React.Component {
render() {
const Wrap = withMyHOC(MyComponent);
return (
<div>
{/* Other Code */}
<Wrap />
</div>
)
}
}
Why you shouldn't use it like above is because everytime render method is called a new instance of the MyComponent is created wrapped by HOC called Wrap and hence everytime it be be mounted again instead of going by the natural lifecycle or React.
However if your HOC passes a function as props, you can use it within the render as long as it doens't cause a re-render again otherwise it will lead to a infinite loop.
Also its better to memoize functions which are called in render directly to avoid computation again and again
CodeSandbox Demo
A High Order Component is a function which returns a Component, not jsx. When wrapping a component with an hoc, you're not changing the returned value of your component, you're changing the signature itself. Consider the following hoc
const withFoo = Component => props =>{
return <Component {...props} foo='foo' />
}
withFoo is a function which takes a Component (not jsx) as argument and returns a component. You don't need to call an hoc from render because the values it injects are already inside props of the wrapped component.
An hoc tells how a wrapped component will look like, changes it's definition so the only place to use it is in the component definition itself. Calling an hoc inside render creates a new instance of that component on each render. It's the equivalent of
const Component = () =>{
const ChildComponent = () =>{
return <span> Child </span>
}
return <ChildComponent /> //You're declaring again on each render
}
Use your high order components like this
const Component = ({ foo }) => <div>{ foo }</div>
export default withFoo(Component)
Or
const Component = withFoo(({ foo }) => <div>{ foo }</div>)

Extend React lifecycle hook (e.g add a print statement on every ComponentDidMount)

I want to add some behaviour on a given lifecycle hook of a React application.
For example, adding a console.log('Component is mounted') on every ComponentDidMount of all the components of an application, without having to define it in every one of them (as a decorator for example), sort of like a global extender of that method that adds some code to it. Like that: Extending Vue Lifecycle Hooks but for React.
Anyone has an idea on how to achieve that? Cheers!
You can use hoc. In the root app, apply the higher order component.
Example:
const withMountHOC = WrappedComponent => {
return class extends Component {
componentDidMount() {
console.log('mounted');
}
render() {
return <WrappedComponent {...this.props} />
}
}
}
export default withMountHOC;
In your app component:
const WrappedApp = withMountHOC(App);
ReactDOM.render(
WrappedApp,
document.getElementById('root')
);
Since the parent componentDidMount hook is called after child componentDidMount hook, the HOC componentDidMount will be applied in any nested level of the component.
You may also be interested to see this blog: Replacing Mixins in React.
create CustomComponent.js
import React, { Component } from 'react'
class CustomComponent extends Component {
constructor(props){
super();
}
componentDidMount(){
console.log('component is mounted');
}
render () {
return (
<div>
{this.props.children}
</div>
)
}
}
export default CustomComponent
Now create MyComponent.js that extends CustomComponent.js
import React, { Component } from 'react'
import CustomComponent from './CustomComponent'
class MyComponent extends CustomComponent {
render () {
return (
<div>
Hello from MyComponent
</div>
)
}
}
export default MyComponent;
now you see console , you have log : "component is mounted"
but if you write componentDidMonunt() inside MyComponent.js , you will get log from MyComponent.js

Why componentWillReceiveProps is not called?

At child, I need to listen for state changed at parent, I tried to do so as:
Child
class deleteDriverAlert extends Component {
constructor(props) {
super(props);
this.state = {
show: false
};
}
componentWillReceiveProps(nextProps) {
console.log("componentWillReceiveProps . . . . .");
this.setState({ show: nextProps.dataset.showDeleteAll });
}
render() {
return (
<SweetAlert
warning
showCancel
confirmBtnText="Yes, delete it!"
confirmBtnBsStyle="danger"
cancelBtnBsStyle="default"
title="Are you sure?"
onConfirm={this.props.dataset.onConfirm}
onCancel={this.props.dataset.onCancel}
show={this.state.show}
>
You will not be able to recover this imaginary file!
</SweetAlert>
);
}
}
export default deleteDriverAlert;
then, in the parent, I add the child as:
<deleteDriverAlert data-showDeleteAll={this.state.show_delete_all} data-onConfirm={this.deleteDriver} data-onCancel={this.onCancelDelete} />
now, although I do change the property state show_delete_all at parent, componentWillReceiveProps is not called at child.
Any idea?
A few things, firstly you don't need to set the props into the state of the child component to be able to use them dynamically. The problem is most likely with the way you are changing the state in the parent.
Here is a simple example of the same thing you are trying to do:
The Parent component has its own state and a method to change it which is bound to the parent context and is flipped with a button. Then the state is passed to the child as a prop:
import React, { Component } from 'react';
import Child from './Child';
class Parent extends Component {
constructor(props) {
super(props);
this.state = { show: false };
this.flipShow = this.flipShow.bind(this);
}
flipShow(){
this.setState({show: !this.state.show})
}
render() {
return (
<div>
<p>Parent</p>
<button onClick={this.flipShow}>Show</button>
<Child show={this.state.show} />
</div>
);
}
}
export default Parent;
Then the child simply passes the prop through. Note: in the example below the componentWillReceiveProps is unnecessary but I only put it there to show that it does fire with this set up.
import React, { Component } from 'react';
import SweetAlert from './SweetAlert';
class Child extends Component {
componentWillReceiveProps(nextProps){
console.log("receiving new props");
}
render() {
return (
<SweetAlert
show={this.props.show}
/>
);
}
}
export default Child;
TL;DR
If componentWillReceiveProps isn't firing, it's a problem with the Parent component and the way it is setting the prop value not the child.
I may be wrong, but I don't think react will re-render the child component when the parent component change state, which makes sense because the state is a local thing specific to that component. But since you're not using a state management library it would make more sense to use the new React Context API in this scenario, to pass changing data down to the Child component, and it looks pretty straightforward
https://reactjs.org/docs/context.html

Unable to change state

Expected behaviour: The app should display John at first and then I change the state (name) to George (setTimeout) so that should be displayed.The state doesn't seem to change though.Any ideas?
import React, { Component } from 'react';
class App extends Component {
constructor(props){
super(props);
this.state={name:"John"};
}
render() {
setTimeout(() => {
this.setState=({name:"George"})
}, 2000)
return (
<div>
{this.state.name}
</div>
);
}
}
export default App;
In comments you got the way to make your code working. But I would like to add an answer here with the message that it is not good idea to change the state of your component in the render() function.
As when your state changes render() function will be called, which will call setTimeout, it will again change the state ... so you will go into infinite loop of rendering your component.
The right way of during this is to have you setTimeout in the componentDidMount() function of your component.

Resources