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?
Related
I have a React app that gets initialized as simple as:
let globalTodos = some_fetch_from_localstorage();
...
function changeGlobalTodos() {
globalTodos = another_fetch_from_localstorage();
}
...
ReactDOM.render(<ReactApp todos={globalTodos} />, document.getElementById('app'));
Inside of the app I'm doing the following:
constructor(props) {
super(props);
this.state = {
todos: []
};
}
static getDerivedStateFromProps(nextProps, prevState) {
if (nextProps.todos !== prevState.todos) {
return { todos: nextProps.todos };
} else return null;
}
componentDidUpdate(prevProps, prevState) {
if (prevProps.todos !== this.props.todos) {
this.setState({ todos: this.props.todos });
}
}
The problem is that whenever I update globalTodos, the props on the React app don't get updated: it stays on the initial globalTodos's value.
I have tried playing with getDerivedStateFromProps is being called only on first setup of the props while componentDidUpdate never gets called :-/
What am I missing here?
I can't leave a comment, so I'll just post this here. React won't re-render unless you're updating a state.
I'd make globalTodos a state and add onto it from there using setState, then you can pass that on as a prop to the child component in your case ReactApp. You don't need to change them as states in your child component.
Example:
class Parent extends Component {
constructor(props) {
super(props);
this.state = {
globalTodos: initialFetchedArray
};
}
changeGlobalTodos() {
let newTodos = fetchNewArray;
this.setState({globalTodos: newTodos});
}
ReactDOM.render(<ReactApp todos={globalTodos} />, document.getElementById('app'));
}
//You just need your prop here, here you can do whatever you want to do with the array if it's display you can use map
class Child extends Component {
render {
return(
{this.props.todos}
)
}
}
Really the main thing here is making your globalTodos a state, using setState to change that state, and just passing that state down as a prop.
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>
)
}
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
I'm fairly new to react and struggle to update a custom component using componentDidMount and setState, which seems to be the recommended way of doing it. Below an example (includes an axios API call to get the data):
import React from 'react';
import {MyComponent} from 'my_component';
import axios from 'axios';
export default class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
data: []
};
}
GetData() {
return axios.get('http://localhost:5000/<route>');
}
componentDidMount() {
this.GetData().then(
(resp) => {
this.setState(
{data: resp.data}
)
}
)
}
render() {
return (
<MyComponent data={this.state.data} />
);
}
}
Doing console.log(this.state.data) just below render() shows that this.state.data does indeed get updated (from [] to whatever the API returns). However, the problem appears to be that MyComponent isn't rendered afresh by componentDidMount. From the Facebook react docs:
Setting state in this method will trigger a re-rendering.
This does not seem to be the case here: The constructor of MyComponent only gets called once (where this.props.data = []) and the component does not get rendered again. I'd be great if someone could explain why this is and whether there's a solution or a different way altogether to get the updating done.
UPDATE
I've added the code for MyComponent (minus some irrelevant features, as indicated by ...). console.log(data_array) prints an empty array.
import React from 'react';
class DataWrapper {
constructor(data) {
this._data = data;
}
getSize() {
return this._data.length;
}
...
}
export class MyComponent extends React.Component {
constructor(props) {
super(props);
this._dataWrapper = new DataWrapper(this.props.data);
this.state = {
data_array: this._dataWrapper,
};
}
render() {
var {data_array} = this.state;
console.log(data_array);
return (
...
);
}
}
You are falling victim to this antipattern.
In MyComponent constructor, which only gets called the first time it mounts, passed your empty array through new DataWrapper and now you have some local state which will never be updated no matter what your parent does.
It's always better to have one source of truth, just one state object anywhere (especially for things like ajax responses), and pass those around via props. In fact this way, you can even write MyComponent as a simple function, instead of a class.
class Example extends Component {
state = { data: [] }
GetData() { .. }
componentDidMount() {
this.GetData().then(res =>
this.setState({data: new DataWrapper(res.data)})
)
}
render() { return <MyComponent data={this.state.data} /> }
}
...
function MyComponent (props) {
// props.data will update when your parent calls setState
// you can also call DataWrapper here if you need MyComponent specific wrapper
return (
<div>..</div>
)
}
In other words what azium is saying, is that you need to turn your receiving component into a controlled one. Meaning, it shouldn't have state at all. Use the props directly.
Yes, even turn it into a functional component. This helps you maintain in your mind that functional components generally don't have state (it's possible to put state in them but ... seperation of concerns).
If you need to edit state from that controlled component, provide the functions through props and define the functions in the "master" component. So the master component simply lends control to the children. They want anything they talk to the parent.
I'm not posting code here since the ammendment you need to make is negligible. Where you have this.state in the controlled component, change to this.props.
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.