Trigger a function when action is dispatched from other component - javascript

I am working on a reactjs application and using redux. I am having two components. Say Component A and Component B. In component A I have a table. When I click any row, I dispatcch an action with row's data as parameters of that action. Every time I click a row, this action is dispatched. I want to trigger a function in Component B whenever a row is clicked and that action is dispatched. How can I do it?
Normally we change the data in reducer via action dispatch and then use that data as state in other components. But here i want to trigger a function in Component B whenever a row is clicked in the table of component A.

Dispatch an action in A when row clicked, that sets a boolean value to true. Then use mapStateToProps to re-render the component on Redux state change. Then make a conditional call to the required function inside componentDidUpdate():
class B extends React.Component {
componentDidUpdate() {
const { rowClickedInAComponent } = this.props;
if ( rowClickedInAComponent ) {
functionToBeCalled();
}
}
render() {
}
}
const mapStateToProps = (state) => ({
rowClickedInAComponent: rowClickedInAComponent
// boolean here, you could pass any info such as which row was clicked
});
export default connect(mapStateToProps)(ListingPage);
Comment if you need further clarification.

You can achieve this through couple of ways some are the following.
i) If there is parent child relation just pass activeRow function as a props of child component
class ComponentA extends React.component{
render(){
return(
{/*view, could be table, section, div, etc.*/}
<div>
<button onClick={this.props.handler}>Click me</button>
</div>
)
}
}
class ComponentB extends React.component{
handleClick(){
//do something.
console.log("clicked!");
}
render(){
return(
{/*view, could be table, section, div, etc.*/}
<div>
<ComponentA handler={this.handleClick}/>
</div>
)
}
}
ii)Using redux, create an activeRow key in redux state and subscribe componentB using react-redux's connect method.
Now in componentB add componentWillReceiveProps life cycle hook, and do something in inside componentWillReceiveProps
Ex:
componentWillReceiveProps(nextProps){
//first parameter is updated props.
//check this.props with nextProps.
//do something here.
}

Related

React child component not updating parents state, so second child is not noticing the changes

Curently I have this structure.
Child1 <---- Parent ----> Child2
Parent component has this state
this.state = {
authenticationChecked: true,
isAuthenticated: true,
sideBarRetracted: false
}
this.retract = this.retract.bind(this);
this.unretract = this.unretract.bind(this);
and the following functions
retract() {
this.setState({
sideBarRetracted: true
}, () => {
console.log("retracted")
console.log(this.state.sideBarRetracted) //I can see this changing to true, but the child wont take it into effect, even tho if i hardcode it , it will
});
}
unretract() {
this.setState({
sideBarRetracted: false
})
console.log("unretracted")
}
Which is pased to both childs
<SideBar retract={this.retract} unretract={this.unretract} retracted={this.state.sideBarRetracted} {...this.props}/>
<PrivateRoute authed={this.state.isAuthenticated} path="/skadi" render={(props) => <Skadi retracted={this.state.sideBarRetracted} {...props}/>} />
Now my question is. I supposedly should receive that as a prop in a child component, for example lets say in the sidebar component which is a child. Whose constructor is this, and im supposed to receive the prop there and assign it to the state
constructor(props) {
super(props);
this.state = {
retracted: this.props.retracted
}
}
It works properly so far, but now I have the following issue. This same component has a function onClick() which will change that specific state
<FontAwesomeIcon className="registerIcon" icon={faArrowAltCircleLeft} onClick={this.props.retract} />
<FontAwesomeIcon className="expandicon-retracted" icon={faArrowAltCircleRight} onClick={this.props.unretract} />
But point is, this is not working, the state is not being updated in the parent component. I can change it manually (hardcoding) and I see the child component reacting to the change, but i cant make it react with the onclick() update
At the same time, I have my Child2 component, who has been passed the parent state as well. Will its state update if I update the parent component using CHILD1 ?
I have seen that i should use something like this in the child2 , but well it wont work so far (mainly because parent component isnt updated afaik)
constructor(props) {
super(props)
this.state = {
sideretracted: false
}
console.log("props in skadi")
}
componentWillReceiveProps(nextProps) {
this.setState({ sideretracted: nextProps.sideBarRetracted});
}
You shouldn't set the child state from the props received from the parent component.
Since you are updating the state of child component from the methods bound to the child components it isn't reflected in the parent component.
One thing you could do is pass a method from the parent to update the state of the parent component and the child state gets updated by itself since it will be passed the new state variable from parent as a prop.
You could have the toggleRetract method defined in the parent to set the parent state and pass it as a prop to the child component which would trigger it and set the parent state for you which will be passed down to the children components as props (like how you are passing the state props).
For example
function Toggle() {
const [on, setOn] = React.useState(false)
const toggle = () => setOn(o => !o)
return <Switch on={on} onToggle={toggle} />
}
function Switch({on, onToggle}) {
return (
<div>
<div>The button is {on ? 'on' : 'off'}</div>
<button onClick={onToggle}>Toggle</button>
</div>
)
}
Here Toggle is the parent component and Switch is the child component and the parent component passes the on state as well as onToggle method which the child component uses to display and update the state respectively.
More info here

Child component doesn't get changed prop

I have the following structure:
class Parent extends React.Component {
componentDidMount() {
document.addEventListener('keydown', () => {
this.value++;
})
}
this.value = 0;
render() {
return (<ChildComponent value={this.value} />)
ChildComponent simply renders the value:
class ChildComponent extends Component {
render() {
return (
<div>
{this.props.value}
</div>
);
}}
I don't want to rerender Parent component on every KeyDown event, that's why I don't put a value to the state.
I want ChildComponent rerenders on every KeyDown event, but ChildComponent renders only once because it doesn't get changed value and always shows 0 although this.value is changing.
Is it possible to rerender ChildComponent while changing this.value of the ParentComponent or I should place logic into ChildComponent using setState to render correct result?
You should add the value to parent state. This is simply the way React works. Even if you could manually trigger a rerender on the child component, it would still be 0 as still 0 is provided props. When React detects state has changed on Parent component, it will calculate the virtual dom of all child elements, do a diff on the virtual dom vs the real dom, then selectively update the real dom with those values (only the inner text of the Child component div will actually be new). React is incredibly optimised to handle rerendering using the virtual dom, and you should not worry about performance loss of this situation.
The reason the child component didn’t get called when value changes because you are overriding normal variable value but not react component state. So inorder to call child component when value changes you need to manage value property in component state of parent so whenever you modify it’s value using setState, component will re render
Change
class Parent extends React.Component {
componentDidMount() {
document.addEventListener('keydown', () => {
this.value++;
})
}
this.value = 0;
render() {
return (<ChildComponent value={this.value} />)
}
}
To
class Parent extends React.Component {
constructor(props){
super(props);
this.state = {
value: 0
}
}
componentDidMount() {
document.addEventListener('keydown', () => {
this.setState({value: this.state.value++});
})
}
render() {
return (<ChildComponent value={this.state.value} />)
}
}

Can we pass setState as props from one component to other and change parent state from child component in React?

class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
this.setState=this.setState.bind(this)
}
render() {
return (
<div>
<Hello name={this.state.name} />
<p>
Start editing to see some magic happen :)
</p>
<Child {...this}/>
</div>
);
}
}
child Component
var Child=(self)=>{
return(
<button onClick={()=>{
self .setState({
name:"viswa"
})
}}>Click </button>
)
here I am binding the setState function and send this as props to child component.This will change state of parent from child.Is this proper way?
But this is simple than passing function.If I want change many state change. this will be simple and faster way compare to passing function right ?
The correct way to do this is as simple as yours and does not violate best practices:
class App extends Component {
state = {
name: 'React',
};
render() {
return (
<div>
<Hello name={this.state.name} />
<p>
Start editing to see some magic happen :)
</p>
<Child onClick={() => this.setState({name: 'viswa'})}/>
</div>
);
}
}
const Child=({onClick})=>(
<button onClick={onClick}>Click</button>
);
You shouldn't pass the setState directly, as the setState function will not change the state immediately.
as the documents said:
Think of setState() as a request rather than an immediate command to
update the component. For better perceived performance, React may
delay it, and then update several components in a single pass. React
does not guarantee that the state changes are applied immediately.
setState() does not always immediately update the component. It may batch or defer the update until later. So you'd better to manage the calling of setState function together as there may have competing of mutating parent's state. It is not recommended to pass this function to another component.
Encapsulating the calling of setState function in one class makes your code stronger.
As #yBrodsky said, you should rather pass down a function which does mutate the state. Here is an example:
class App extends Component {
constructor() {
super();
this.state = {
name: 'React'
};
this.update=this.update.bind(this);
}
update(nextState) {
this.setState(nextState);
}
render() {
return (
<Child updateParent={this.update} />
);
}
}
const Child = ({updateParent}) => (
<button onClick={() => updateParent({name: 'Foo bar'})}>Click</button>
);
You now have full control over the state of the Parent in the child. Just pass the object to be shallowly merged into the parent state. In this example, name in App will change to Foo bar on button click.
i guess in functional components its possible to send your setState to childrens and change parent state in them but not in class base components

React Router v4 previous state component

I'm looking for a solution - react router v4 doesn't hold the previous state of component. For example, here is a simple counter:
import React, { Component } from "react";
class Schedule extends Component {
constructor(props) {
super(props);
this.state = {
counter: 0
};
this.holdState = this.holdState.bind(this);
}
holdState() {
this.props.location.state = this.state.counter;
const state = this.state;
this.setState({
counter: state.counter + 1
});
}
render() {
return (
<div>
<ul>
<li>6/5 # Evergreens</li>
<li>6/8 vs Kickers</li>
<li>6/14 # United</li>
</ul>
<div>
<button onClick={() => this.holdState()}>Click</button>
<span>{this.state.counter}</span>
</div>
</div>
);
}
}
export default Schedule;
I was trying to push the state into location and history by props.
But whenever I press "browser button back" from the next page, it always resets the state.
Can someone tell me where I'm doing a mistake?
Whenever your component mounts, your constructor will be initiated which will reset the state back to 0. This happens when you are on another component and press back button in which case your current Route gets mounted.
Also directly mutating the location.state value is not a right approach. What you need is to save your value in localStorage and refill it in state if it exists.
Another thing is that when you wish to update the state based on prevState, you could make use of functional setState. Check this for more details:
When to use functional setState
import React, { Component } from "react";
class Schedule extends Component {
constructor(props) {
super(props);
this.state = {
counter: localStorage.getItem('counter') || 0
};
this.holdState = this.holdState.bind(this);
}
holdState() {
localStorage.setItem('counter', this.state.counter);
this.setState(prevState => ({
counter: prevState.counter + 1
}));
}
render() {
return (
<div>
<ul>
<li>6/5 # Evergreens</li>
<li>6/8 vs Kickers</li>
<li>6/14 # United</li>
</ul>
<div>
<button onClick={() => this.holdState()}>Click</button>
<span>{this.state.counter}</span>
</div>
</div>
);
}
}
export default Schedule;
I was trying to push state into location and history by props. BBut whenever I > press "browser button back" from the next page, it always resets the state.
when you call setState you're not changing the route, you're just triggering a rerender of your component.
If you press the back button after incremented the counter react router will symply pop the last route from the history stack but since you don't have a previous route the component will be remount hence the resetted state.
To implement what i suppose you want to achieve you need to explicitely change the route every setstate (e.g. adding a parameter in the query string with the current value of the counter, like ?counter=1, ?counter=2..) , this way you'll be sure that a new route will be push on top of the stack every setState and the back button will then work as you expect.

Letting a child know that it should update its state in React

I'm trying to let a child Component know that it should update its state after a prop change in the parent.
There is no state that needs to be shared between the two. The only thing that needs to happen is that the parent should somehow let the child know that it needs to update its state (literally call setState by itself with the info it already has).
So far I can only figure out to do in the "React"-way through the componentWillReceiveProps and sending some arbitrary props, like a number, to let the child know that it should call the function to set the state.
Another way would be to use signals to let the child know, but this seems a bit over the top for this situation.
So in summary:
The parent needs to let the child know that it should call a function
The function will update the state (setState) of the child
There is no need for the child to receive any information from the parent
Can anyone help me figure out the best way to do this?
As you can see in the snippet, this is more or less the situation. I would like to know the best way to have the Child component call the _updateState function when the Parents props have changed (does not happen in the snippet right now).
//Imagine this is the redux-container that passes the store state to the parent.
class ParentWrapper extends React.Component {
constructor(){
super();
this.state = {status: 'normal'};
}
//This would be an action to the reducer that would update the store state
_updateStatus(){
this.setState({status: 'updated'});
}
render(){
return (
<div>
<button onClick={this._updateStatus.bind(this)}>Click me</button>
<Parent status={this.state.status} />
</div>
);
}
}
class Parent extends React.Component {
render(){
return (
<div>
<Child />
</div>
);
}
}
Parent.propTypes = {
status: React.PropTypes.string
};
Parent.defaultProps = {
status: 'normal'
};
class Child extends React.Component {
constructor(){
super();
this.state = { test: 1 };
}
_updateState(){
this.setState({test: this.state.test + 1});
}
render(){
return (
<div>Child: {this.state.test}</div>
);
}
}
ReactDOM.render(<ParentWrapper />, document.getElementById('container'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.0.2/react-dom.min.js"></script>
<div id="container"></div>
EDIT: added snippet.
You can use refs to access all the methods under the child component.
See the following fiddle
https://jsfiddle.net/pranesh_ravi/412j5ucw/
Here using refs, I'm calling a function inside the child which will change the state of the child component.

Categories

Resources