Updating Child Component Via Change of Props from Parent in React - reactjs

I have a component whose state is passed as a prop to a child component. I noticed that even if the Parent's state is changed, the child component is not re-rendered.
Based on the official React Documentation for utilizing shouldComponentUpdate:
"The first time the inner component gets rendered, it will have { foo:
'bar' } as the value prop. If the user clicks on the anchor, the
parent component's state will get updated to { value: { foo: 'barbar'
} }, triggering the re-rendering process of the inner component, which
will receive { foo: 'barbar' } as the new value for the prop.
The problem is that since the parent and inner components share a
reference to the same object, when the object gets mutated on line 2
of the onClick function, the prop the inner component had will change.
So, when the re-rendering process starts, and shouldComponentUpdate
gets invoked, this.props.value.foo will be equal to
nextProps.value.foo, because in fact, this.props.value references the
same object as nextProps.value.
Consequently, since we'll miss the change on the prop and short
circuit the re-rendering process, the UI won't get updated from 'bar'
to 'barbar'."
If I can't use shouldComponentUpdate, how would I force the child component to re-render based on a change of props from the Parent?
Parent Component
I want the child component to rerender based on the boolean given to showDropzone.
<Dropzone
onDrop={this.onDrop}
clearFile = {this.clearFile}
showDropzone = {this.state.filePresent}/>
Child Component
export default class Dropzone extends Component {
constructor(props) {
super(props);
}
render() {
const { onDrop } = this.props;
const {showDropzone} = this.props;
const {clearFile} = this.props;
return (
<div className="file-adder">
<div className="preview">
{{showDropzone}?
<Dropzone
onDrop={onDrop}
ref="dropzone">
</Dropzone>
:<button type="button"
className="btn btn-primary"
onClick={clearFile}>
Clear File</button>}
</div>
</div>
);
}
}

I'am sorry but your code has a lot of errors
First (This is a tip) inside:
const { onDrop } = this.props;
const { showDropzone } = this.props;
const { clearFile } = this.props;
You can write just one line
const { onDrop, showDropzone, clearFile } = this.props;
Now your problems starts from here:
You used showDropzone inside curly brackets and this is your
problem remove them
Your mistake {{showDropzone}? firstOption : anotherOption }
Will be { showDropzone? firstOption : anotherOption }
Another mistake:
You used the Dropzone component inside itself and this is a big mistake
You can't use Dropzone component here {{showDropzone}? <Dropzone {...}/> : anotherOption } You can use it from another component
Finally i tried to format your code to make it like this
Parent Component
{ this.state.filePresent
? <Dropzone onDrop={this.onDrop} />
: <button
type="button"
className="btn btn-primary"
onClick={clearFile}
children="Clear File"
/>
}
Child Compnent
class Dropzone extends Component {
render() {
const { onDrop } = this.props;
return (
<div className="file-adder">
<div className="preview" onDrop={onDrop} ref="dropzone">
<p>Hi! This is Dropzone component</p>
</div>
</div>
);
}
}
export default Dropzone;

In child component you have used "showZone" instead of "showDropzone".
Thanks

Related

onclick event for Imported component in react?

I have imported a component from a different file and I want to reset my timer if I click on the imported component's elements. Is there a way to tackle this issue or should I write both components in single jsx ?
import {SampleComponent} from "../SampleComponent";
<div>
<SampleComponent onClick = {?????????}/>
</div>
What you can do here is,
import {SampleComponent} from "../SampleComponent";
<div onClick={??????}>
<SampleComponent/>
</div>
Or you can pass the function from your parent component and add click event on top node of the child component.
<div>
<SampleComponent onHandleClick={() => ??????}/>
</div>
If you want to call a function in parent component, whenever an event (such as in your case an onClick event) occurs in a child component, you will need to pass the parent function as a props.
Here's what it will look like:
class ParentComponent extends React.Component {
handleClick = () => { ... }
render {
return (
<SampleComponent onClick={this.handleClick} />
)
}
}
And here is how your SampleComponent will be:
class SampleComponent extends React.Component {
render {
return (
<div onClick={this.props.onClick}> //to call the function on whole component
<button onClick={this.props.onClick}>Click Me</button> //to call the function on a specific element
</div>
)
}
}
What I have understand so far from your question is that you want to call a function in SampleComponent whenever a click event occurs on it (on SampleComponent).
To do this, here is how your SampleComponent will look like :
class SampleComponent extends React.Component {
.
.
render() {
handleClick = () => { ... }
return (
<div onClick={this.handleClick}>
...
</div>
)
}
Note: For this you don't need to add onClick in parent.
resetTimerHandler = (timer)=>{
timer = 0; or this.setState({timer: 0}) // how ever you are defining timer
}
render(){
let displayTimer;
this.updateTimer(displayTimer)// However you are updating timer
return(
<SampleComponent onClick={this.resetTimerHandler.bind(this,displayTimer)} />)
Without knowing how you are updating your timer I can't really give a specific answer but you should be able to apply this dummy code.
It's hard to answer your question specifically without more context (like what timer are you wanting to reset). But the answer is no, you do not need to implement both components in the same file. This is fundamental to react to pass props like what you tried to do in your question.
Here is an example.
Say your SampleComponent looks like the following:
// SampleComponent.jsx
function SampleComponent({ onClick }) { // the argument is an object full of the props being passed in
return (
<button onClick={(event) => onClick(event)}>Click Me!</button>
);
}
and the component that is using SampleComponent looks like this:
import SampleComponent from '../SampleComponent';
function resetTimer() {
// Add logic to reset timer here
}
function TimerHandler() {
return (
<SampleComponent onClick={() => resetTimer()} />
);
}
Now when you click the button rendered by SampleComponent, the onClick handler passed from TimerHandler will be called.
A prop on a React component is really just an argument passed into a function :)

Get the child's props onClick in parent component

I have a parent ButtonGroup component and the child buttonItem component:
//ButtonGroup Component (Parent)
clicky(){
//capture the props of the clicked button, ie, caption and disabled here.
}
render() {
return (
<div onClick={this.clicky}>
{this.props.children}
</div>
)
}
//buttonItem component:
render() {
return (
<button disabled={this.props.disabled}>{this.props.caption}</button>
)
}
//final render
<ButtonGroupComponent>
<buttonItem caption="Nothing"/>
<buttonItem caption="Something" disabled={true}/>
<buttonItem caption="Refresh"/>
</ButtonGroupComponent>
from the above code is there any way i can capture the props of the clicked child buttonItem?
In your case, you need to merge this.props.children with your custom prop. So, I suggest you use React.Children to operate with it.
By the way, after adding new prop you need to return this child, so cloneElement will help you with this.
Inside import section of ButtonGroupComponent:
import React, { Children, Component, cloneElement } from 'react';
Its render function will look like this:
render() {
const childrenWithCustomHandler = Children.map(this.props.children, itemChild => {
// Arguments of cloneElement (component, [customProps], [customChildren])
return cloneElement(itemChild, { onClickItem: this.clicky })
}
);
return <div>{childrenWithCustomHandler}</div>;
}
And the code of buttonItem component will look like:
return (
<button
disabled={this.props.disabled}
onClick={() => {
this.props.onClickItem({ ...this.props });
}}
>
{this.props.caption}
</button>
)
I used Spread operator to clone the object, so if you will want to change props in your clicky function, the child won't be rendered.

Using React, is it possible to define a component inside the definition of another component?

(this question differs to 'is it possible to use a component inside another', this question is 'can I define a component inside the definition of another', it is not a duplicate of 'Can I write Component inside Component in React?')
Is it possible to define a component inside the definition of another component? This way I can use the props of the outside component in the inner component. It would keep the code more concise. Something like the following...
class AComponent extends Component {
CustomButton = (props) => {
let { disabled, ...otherProps } = props // <-- props of the inner component
const {isDisabled, currentClassName} = this.props // <-- props of the main component
return (
<button
className={className}
disabled={isDisabled}
{...otherProps}>
</button>
)
}
render() {
return (
<div>
<CustomButton>Add something</CustomButton>
<CustomButton>Delete something</CustomButton>
<CustomButton>Edit</CustomButton>
</div>
)
}
}
If the custom button was defined on its own (the usual way of defining components) I would have to do something like below which is ok but more verbose and less dry as I repeat the definition of {...buttonProps} for each component
let buttonProps = {
className: this.props.currentClassName,
disabled: this.props.disabled
}
return (
<div>
<button {...buttonProps}>Add something</button>
<button {...buttonProps}>Delete something</button>
<button {...buttonProps}>Edit</button>
</div>
)
While yes, it's possible to define one function component inside another function component, this is not recommended.
Referring to the ReactJs docs:
reactjs.org/docs/components-and-props
reactjs.org/docs/conditional-rendering
Most, if not all, examples show child components being defined outside of the parent component.
Defining a component inside another will cause the child component to be re-created on mount and unmount of the parent, which could cause unexpected behavior if the child is using props from the parent, and cannot handle if those props are suddenly undefined.
It's best to define components separately.
Yes! I needed to define the function component in the render method...which makes sense
class AComponent extends Component {
render() {
const ButtonLocal = (props) => {
const {isDisabled, currentClassName} = this.props
return (
<Button
disabled={isDisabled}
className={currentClassName}
{...buttonProps}>
</Button>
)
}
return (
<div>
<ButtonLocal>Add something</ButtonLocal>
<ButtonLocal>Delete something</ButtonLocal>
<ButtonLocal>Edit</ButtonLocal>
</div>
)
}
}
Very much possible to add components inside a parent component. Consider following example:
<ParentComponent style={styles}>
<ChildComponent style={styles} {...props} />
</ParentComponent>

Render a component when another component is clicked

I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. I know how to set state in the component itself, but how do I affect another component when I click a different component?
class BlackSpark extends React.Component {
render() {
return (
<div className="black"></div>
);
}
}
class RedSpark extends React.Component {
render() {
return (
<div className="red"></div>
);
}
}
class App extends React.Component {
render() {
return (
<div>
<BlackSpark />
<RedSpark />
</div>
);
}
}
In React, there's a concept of component composition as you've already embraced -- it allows you to accomplish what you want by rendering children based on the parent's state, another key concept known as lifting state up. What this means, is if you have mutually dependent components, create a single parent which composes them, and have state in the parent control the presentation and logic of the children. With the parent App, you can keep your state inside App, and based on App's state, conditionally render whatever you want -- either BlackSpark or both. For example, using the logical && operator:
{condition && <Component />}
This will only render <Component> when condition is truthy, or else it will not render anything at all (except for when condition is 0). Applying it to this situation, try adding state to your App component to utilize conditional rendering.
There's another key concept you need to understand: component props. They are essentially inputs to a component, certain properties passed to the component to tell how it should behave -- like attributes on regular HTML elements such as input placeholders, URLs, and event handlers. For example:
<Component foo="bar" bar={3} />
This will pass the props foo and bar down to Component with the values "bar" and 3 respectively and are accessible through this.props. If you were to access this.props.foo inside the Component component it would give you "bar". If you pair this up with composition, you can accomplish what you want:
class Example extends React.Component {
constructor() {
super();
this.state = {
showHello: true
}
this.handleChange = this.handleChange.bind(this);
}
handleChange() {
this.setState(prevState => ({
showHello: !prevState.showHello
}));
}
render() {
return (
<div>
{this.state.showHello && <Child2 />}
This is a test.
<Child1 onClick={this.handleChange} />
</div>
);
}
}
class Child1 extends React.Component {
render() {
return <div onClick={this.props.onClick}>Click me!</div>
}
}
class Child2 extends React.Component {
render() {
return <div>Hello!</div>
}
}
ReactDOM.render(<Example />, document.getElementById('root'));
<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>
<div id="root"></div>
The above example lifts state up by having a parent compose the children and maintain the state. It then uses props to pass down an onClick handler to Child1, so that whenever Child1 is clicked, the state of the parent changes. Once the state of the parent changes, it will use conditional rendering to render <Child2> if the condition is truthy. Further reading at the React documentation and on the logical && operator.
I know how to set state in the component itself, but how do I affect another component when I click a different component?
The recommended way to do it would be to create a parent component that has the state. You'd then use that state to determine when to render the other child component.
I want to render BlackSpark when RedSpark is clicked, but I'm not sure how to change the state of a component in another component. Also, what if I want to hide BlackSpark when GreenSpark is clicked and GreenSpark is inside BlackSpark?
In this case, here's how you'd do it.
const GreenSpark = ({ onClick }) => (
<button className="green" onClick={onClick}>X</button>
)
const BlackSpark = ({ onClick }) => (
<div className="black">
<GreenSpark onClick={onClick} />
</div>
)
const RedSpark = ({ onClick }) => (
<div className="red" onClick={onClick}></div>
)
class Spark extends React.Component {
constructor(props) {
super(props)
this.state = {
showBlack: false
}
this.boundShowBlack = this.showBlack.bind(this)
this.boundHideBlack = this.hideBlack.bind(this)
}
showBlack() {
this.setState({ showBlack: true })
}
hideBlack() {
this.setState({ showBlack: false })
}
render() {
return (
<div>
<RedSpark onClick={this.boundShowBlack} />
{this.state.showBlack && <BlackSpark onClick={this.boundHideBlack} />}
</div>
)
}
}

Get another component input value React js

I have two components one is app component and other one is sidebar component i have been using input field in side bar and i want to get the value of that input field in my app component on click how this could be possible ?
You can try lifting the state up.
Create a new component that will contain your two components. In that new component, create a function that you will pass as props inside the sidebar component.
class ContainerComponent extends React.Component {
constructor() {
super();
this.state = {
valueThatNeedsToBeShared: ''
}
}
handleChange(e) {
this.setState({valueThatNeedsToBeShared: e.target.value})
}
render() {
return (
<div>
<AppComponent value={this.state.valueThatNeedsToBeShared} />
<SidebarComponent handleChange={this.handleClick.bind(this)} value={this.state.valueThatNeedsToBeShared} />
</div>
)
}
}
const SidebarComponent = ({handleChange, value}) => <aside>
<input value={value} onChange={handleChange} />
</aside>
const AppComponent = ({value}) => <div>
value from sidebar: {value}
</div>
In pure react it is possible by adding callback from one component and use it into parent component to change their state and then send input value from parent's state to props of your second component

Resources