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

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.

Related

Render Only Child Component Only On Prop Change

I want to re-render my child component without re-rendering the parent component when the parent's state changes.
In this example, componentWillReceiveProps is never called.
Thank You!
Parent Component
export default class Parent extends Component {
constructor(props) {
super(props);
this.state = {
myValue: 'hello'
}
}
shouldComponentUpdate(nextProps, nextState) {
return false;
}
myFunction() {
this.setState(prevState => {
return {myValue: 'world'};
});
}
render() {
return (
<View>
<Button onPress={myFunction} title="Learn More"/>
<Child myText={this.state.myValue}/>
</View>
);
}
}
Child Component
export default class Child extends Component {
constructor(props) {
super(props);
}
componentWillReceiveProps(nextProps) {
console.log('This Is Never Called');
}
render() {
return (
<View>
<Text>{this.props.myText}</Text>
</View>
);
}
}
There is no way to do what you are proposing explicitly. When you define:
shouldComponentUpdate(nextProps, nextState) {
return false;
}
are telling Parent never to rerender after its initial render. However the passing of props to Child (and therefore the impetus for Child to rerender) happens inside the render method of Parent. So when you block rerending on Parent you are also blocking rerendering on all children of Parent.
There is, however, no need to block rerendering on Parent because React will modify the DOM as little as possible, so you will only see changes in parts of parent that need to be modified (due to a change of state). As long as all the props being passed to the other children of Parent (other than Child that is) remain unchanged, only Child will be modified in a Parent.render call.
Basically React already handles what you are trying to do.
In order to re-render child component with new props, parent component has to be re-rendered. There are generally no reasons to not re-render parent component in this situation. React was designed to do this efficiently and reuse existing DOM elements on re-render where possible.
The alternative approach is to make child component re-render its children and make parent component trigger the update somehow. This can be done with refs, for instance:
export default class Parent extends Component {
state = {
myValue: 'hello'
}
childRef = React.createRef();
myFunction = () => {
this.childRef.current.setText('world');
}
render() {
return (
<View>
<Button onPress={this.myFunction} title="Learn More"/>
<Child ref={this.childRef} myText={this.state.myValue}/>
</View>
);
}
}
And child component maintains its own state:
export default class Child extends Component {
static getDerivedStateFromProps({ myText }, state) {
return {myText, ...state};
}
setText(myText) {
this.setState({ myText });
}
render() {
return (
<View>
<Text>{this.state.myText}</Text>
</View>
);
}
}
Here's a demo.
This is acceptable solution but it results in less straightforward design. 'Dumb' Child like in original code is a preferable way to do this which shouldn't be changed for optimization reasons in general. If there are problems with re-rendering Parent children, they possibly should be addressed in another way.

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

Redux: passing information to/from window child

Essentially I am trying to create an app where it has a note-pad feature that opens up a window child and passes some information from the parent (which holds the redux state) to it.
However, I am having trouble on how to send the information from the child to the parent, specifically dealing with dispatching action.
I was able to figure it out on passing from parent to child as so without using Redux:
Parent Window
class NavBar extends React.Component {
constructor() {
super()
this.handleNotesMenu = this.handleNotesMenu.bind(this)
}
handleNotesMenu() {
window.id = this.props.id
window.userName = this.props.userName
window.currentReduxState = store.getState()
const notePad = window.open('NotePad', 'notes', 'toolbar=0,status=0,width=715,height=325')
}
Child Window
export class NotePad extends React.Component {
constructor(props) {
super(props)
this.handleChange = this.handleChange.bind(this)
this.state = {
notes: window.opener.state.getIn(['plan', 'notes'])
}
}
handleChange(tabID) {
return e => {
const state = {}
state[tabID] = e.target.value
this.setState(state)
}
}
render() {
return (
<textarea
id="saveArea"
value={this.state.notes}
onChange={this.handleChange('notes')}
name="textarea"
/>
)
}
}
I thought out about that adding action dispatcher to the child was hard, so I was thinking somehow incorporate with sessionStorage. But then I got stumped on how the parent window is able to listen to listenStorage on the fly.
Any thoughts? What would be the best practice when it comes to dealing with window child in React/Redux?
Send a method as prop which will take the returning data as argument from child component and call this method in your childcomponent with that data...like this....
In parent component.....
someMethod(data){
// do something here with data
}
In your render method pass this method to child component as...
<ChildComponent someMethod={this.someMethod} />
Now in your child component call that method like this...
this.props.someMethod(someData);
And that's it your are done...
You have passed the data your parent component without dispatch ... In that someMethod() you can do whatever you wanna do with that data

componentWillReceiveProps not being called

I'am updating the state messages and id of TalkContent and I'am trying to pass it to the appropriate child of Talks (which is itself a child of TalkContent).
TalkContent
class TalkContent extends React.Component{
constructor(props){
super(props)
this.state = { talks : [], id : -1, messages : []};
//
}
render(){
if (this.state.id === -1)
{
return(
<div className="TalksList">
<Talks talks={this.state.talks} onTalkClick={this.onTalkClick} messages={this.state.messages} talkId={this.state.id} />
//
</div>
)
}
Talks
class Talks extends React.Component{
render(){
const talks = this.props.talks.map((talk, i) => {
return (
<Talk
key={i}
id={i}
talk={talk.talk}
username={talk.username}
date={talk.date}
onTalkClick={this.props.onTalkClick}
messages={this.props.messages}
talkId={this.props.talkId}
/>
)
});
return(
<div className="talkList">
{talks}
</div>
)
} //render
}
Talk
class Talk extends React.Component{
constructor(props){
super(props);
this.state = { messages : []};
this.handleClick = this.handleClick.bind(this);
}
render(){
return(
<div>
{this.props.username} : <span onClick={this.handleClick}>{this.props.talk}</span> {moment(this.props.date).fromNow()}
</div>
)
}
componentWillReceiveProps(nextProps) {
if(this.props.id === this.nextProps.talkId){
this.setState({messages: nextProps.messages});
}
}
However, even when the states messages and id of TalkContent (the parent component) are updated, the function componentWillReceiveProps of Talk is never fired, in any of the childs. Can someone tell me what is wrong please?
I've quickly made a code sandbox that shows how a parent component can talk to a child component. If you open up Developer Tools > Console, you can see the props updating from both Parent and Child component. Hope it helps and feel free to ask any questions.
Example for Mit
According to the discussion. It seems props values were changed in the parent level(TalkContent Component) and props change was expected to fire in the Grand Child Component (Talk Component) through componentWillReceiveProps function.
componentWillReceiveProps gets normally fired within the child component if the props are changed in the parent component. So make-sure implementing componentWillReceiveProps function should happen within parent-child hierarchy only
React doesn't automatically rerender the component Talks if this.props are changed, you need to manually trigger a rerender using this.forceUpdate() or by setting state on it and updating that.

ReactJS: Accessing Child Final State

This may seem as a bit of a redundant question but I'm trying to access the final state of a child in React, after it has updated. I've been looking into the React LifeCycle docs (I think that might be the issue, but not sure), searched high and low, and can't quite figure it out.
I've got a component which needs to access the (final) value of the state of a child, once that child has done some updating (AJAX request which then does a few this.setStates).
So far, I'm able to access the entire state of that child, accessing through a ref (Inside componentDidMount), but when I try to access a specific value of said state, it returns null or undefined.
Here's some example code to explain (I'll try to spare you as much useless code as possible):
class Layout extends Component {
constructor(props) {
super(props);
}
componentDidMount(){
// This gives me the updated State where pageTitle = "Whatever"
console.log(this.refs.child1);
// However this gives me the initial State where pageTitle = null
console.log(this.refs.child1.state.pageTitle);
}
render(){
return (<div>
{React.cloneElement(
this.props.children,
{ref: 'child1'}
)}
</div>);
}
}
And here's the child component for reference (note: i'm using axios for my ajax requests):
export class ChildComponent extends Component {
constructor(props) {
super(props);
this.state = {
resultData: result,
pageTitle: null
}
}
componentDidMount(){
this.serverRequest = axios.get(apiUrl)
.then(function(result){
this.setState({
resultData: result,
pageTitle: result.pageTitle
});
}.bind(this))
}
render(){
return(<div>
{use of different this.state.resultData values works fine here}
</div>)
}
}
Appreciate any help that comes this way
To use a callback, add this code to the parent element:
handleAsyncDone(data) {
// Do whatever it is people do with data
}
And then pass that function to the child component, and in the childcomponent, add
this.props.handleAsyncDone(this.state);
Which will pass the child state back up to the parent.

Categories

Resources