I am trying to pass a parent's state down into a grandchild's props. The thing is, I am creating the grandchild component in the child's constructor/state variable, and it doesn't seem to want to pass the props down. The child successfully receives the props results object, but the grandchild has an empty object when I reference this.props.results. How can I send the props down to the grandchild?
Parent Component
render()
{
return( <ChildComponent results={this.state.results}/> );
}
Child Component
constructor(props) {
super(props);
this.state = {
containerContent: [
<GrandChildComponent results={props.results}/>, //this.props.results also does not work
]
}
}
render()
{
<main>
{this.state.containerContent[0]}
</main>
}
Grandchild Component
render()
{
<h3>{this.props.results.someValue}</h3> //Throws an error, saying `results` is empty.
}
You can use context api or else use redux.
which is more convenient.
Related
This is my parent code:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
tags: [],
};
}
componentDidMount() {
this.getTags();
}
getTags() {
//method gets tags from the backend
}
render() {
return <Child tags={this.state.tags} />;
}
}
And this is basically my child component:
export default class Child extends Component {
constructor(props) {
super(props);
this.state = {
tags: props.tags,
};
}
componentWillReceiveProps(nextProps) {
this.setState({
tags: nextProps.tags,
});
}
}
But when I console log tags somewhere in the Child component, it is undefined. Maybe it is undefined because the child component gets rendered before the parent component calls the method getTags? Or is there any other problem with this code? And how can I avoid this problem that tags are undefined in the child component?
Cheers
To avoid your problem, you shouldn't be rendering your Child component until the this.state.tags has any useful values.
Here is how you can do it and also show a "Loading..." text, so the user isn't worried the page is broken.
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
tags: [],
};
}
componentDidMount() {
this.getTags();
}
getTags() {
//method gets tags from the backend
}
render() {
return this.state.tags.length ? (
'Loading...'
) : (
<Child tags={this.state.tags} />
);
}
}
Your child component will definitely get rendered with the empty 'tags' array as a prop. Then, when getTags() returns the data, the newly populated tags array will be passed to the child as a prop, forcing the child to get re-rendered with the new data.
It should be the empty array though, not "undefined". You might check your getTags() method and the API you are calling to make sure you aren't getting "undefined" from there.
componentWillReceiveProps is legacy and should not be used. See the following link in the React docs for details: https://reactjs.org/docs/react-component.html#unsafe_componentwillreceiveprops
That documentation will walk you through what to do if you need to perform side effects as a result of changing props.
Right now the only thing is componentWillReceiveProps is to set local state to the props, which is totally superfluous. Is there something else you are needing to do there?
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
i'm trying to pass the value entered by the user from the app component to the passTicket component. I tried invoking props to pass this state data but I keep getting an undefined error when attempting to access it. I'm new to react and it would be great if someone can help me make sense of what i'm getting wrong. This is a sample of what i'm trying to achieve.
This is my main component:
class App extends Component {
constructor(props){
super(props);
this.state = {
ticket:"",
};
this.changeTicket = this.changeTicket.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
this.keyPress = this.keyPress.bind(this);
}
changeTicket(e){
this.setState({
ticket : e.target.value,
})
}
handleSubmit(){
this.setState({
updatedTicket: this.state.ticket
});
}
keyPress(e){
if (e.keyCode ===13){
this.handleSubmit();
}
}
render() {
return (
<div className="App">
<header className="App-header">
<input type="text" placeholder="ENTER TICKET NUMBER" value={this.state.ticket} onKeyDown={this.keyPress} onChange={this.changeTicket}/>
</header>
</div>
);
}
}
and i'd like to be able to store the updatedTicket value in a variable which I can use in my PassTicket component. this is what i've attempted so far but the error it occurs is the following Uncaught TypeError: Cannot read property 'updatedTicket' of undefined
this is what my second component looks like:
class PassTicket extends Component {
transferredTicket(){
const myTicket = this.props.state.updatedTicket;
return myTicket
}
render() {
return (
<p>{this.transferredTicket()}</p>
);
}
}
When passing down a property from a parent to a child component, the property will be stored onto the props by the name it's passed through. For example:
class Parent extends Component {
state = {
ticket: '',
}
render() {
return <ChildComponent updatedTicket={this.state.ticket} />
}
}
class ChildComponent extends Component {
static propTypes = {
updatedTicket: PropTypes.string,
}
static defaultProps = {
updatedTicket: '',
}
render() {
return (
<div>{this.props.updatedTicket}</div>
);
}
}
In the example you've given, it doesn't seem like you're passing the state down to the component you're trying to access it in. In addition, it seems like you're trying to access the updatedTicket as a property of a state object, so just beware of how you're accessing your props.
Therefore, in order to access the updatedTicket property on the child component, you'll first need to import the PassTicket component, instantiate it in the parent (App) component, and pass the property down:
<PassTicket updateTicket={this.state.ticket} />
You would then be able to access the string in the PassTicket component like so - this.props.updateTicket
So .state in react is a local state that is only visible to the individual component. You can read more about it here: https://reactjs.org/docs/state-and-lifecycle.html
In order to pass your state around, you need to use the props system. So where you instantiate your component, you can pass in the state of the parent. For example:
<PassTicket ticket={this.state.updatedTicket}/>
Then inside your PassTicket render function, you can access the ticket prop:
render() {
const { ticket } = this.props
return (
<div>{ticket}</div>
)
}
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.
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.