0

I am trying to set an initial state for a component, from a prop that I have passed through. The prop is passed from App.js where it is populated from an API call.

When I check for a value for the component state there is none. How could this be?

Here is code setting initial state from ListBooks.js

class ListBooks extends Component {
constructor(props) {
    super(props);

    this.state = {
      x: props.books
    };
    console.log("Check if state has been set")
    console.log(this.state.x)
}

Here is where prop is populated in App.js

class BooksApp extends React.Component {
  state = {
    books: []
  }

componentDidMount() {
  console.log("Fetching All Books from API")
  BooksAPI.getAll().then((books) => {
    this.setState({books})
  })
}

My overall goal is to be able to update the local state in the ListBooks component - so it updates immediately (haven't finished the code for this part) and then to update the server in background via API. To save a long reload after each update.

Robinj
  • 31
  • 1
  • 5
  • How do you call ListBooks? Did you do it in BooksApp like so ? – dnp1204 Mar 22 '18 at 18:23
  • Why do you need to store it in the local state? Can't you directly use it from the props? This is one of the anti-patterns. You might want to take a look at [this question](https://stackoverflow.com/questions/40063468/react-component-initialize-state-from-props). If this is absolutely needed only then you should initialize state from props. [Reference to React Docs](https://reactjs.org/docs/react-component.html#constructor) – Hardik Modha Mar 22 '18 at 18:25
  • Well I can use it directly from the props - and I have this part working. However when someone makes a change on the page, I want the page to re-render immediately. Otherwise I have to update server by passing change back to prop method...so page will take time to reload. What I want is user makes change, update component state immediately so page re-renders, then in background update API - so the two are aligned, but without having to wait for server calls. Hope that makes sense – Robinj Mar 22 '18 at 18:46

2 Answers2

1

You shouldn't do this for sure.

First of all read this tutorial in the docs. It will show you the right way to organize your state.

If you can get values which you need from props - you shouldn't keep it in state - just use props in render.

My overall goal is to be able to update the local state in the ListBooks component - so it updates immediately (haven't finished the code for this part) and then to update the server in background via API. To save a long reload after each update.

This is not a good idea because you need to be sure that data is changed on the server sucessfully otherwise you will display not reliable data which can be confused for user.

For example: Let's imagine you have a list of books which you got from a server. Then user add new book to this list and you update state immediately and then send request to the API and something went wrong there (you have sent invalid data, for example). After that user decided to change the info of this newly added book but when he/she will try to do it - server will, probably, return 404 because there is no such book. It's kinda confusing, isn't it? Does it makes sense? I hope so.

Good luck and enjoy your coding :)

Denys Kotsur
  • 2,579
  • 2
  • 14
  • 26
  • Thanks! Very helpful...but say for comparison, im on settings page of my user profile on some website, I make an account change from a drop down menu - and it updates immediately without having to hit submit. Isnt that website doing what I wanted to achieve? Updating local state while updating server in background – Robinj Mar 22 '18 at 18:56
0

console.log(this.state.x) is undefined since the following code:

componentDidMount() {
  console.log("Fetching All Books from API")
  BooksAPI.getAll().then((books) => {
    this.setState({books})
  })
}

Includes two asynchronous calls that are not guaranteed to complete before it is rendered: BooksAPI.getAll() and setState.

But as the second comment to your question indicated, it is strange that you set the state in ListBooks from the passed props. The point in passing props is not to need to set local state in the component that gets the props.

If, as you wrote in the 4th comment on the question, you modify it inside ListBooks, then you probably need to call BooksAPI inside ListBooks and not pass props.

Yossi
  • 5,577
  • 7
  • 41
  • 76
  • Any suggestions on what a better approach would be ? Or how I can ensure its loaded before hand – Robinj Mar 22 '18 at 18:50
  • The better approach is, as said by Hardik Modha, to remove state from ListBooks. I don't understand your comment above (4th comment on the question). How will it help you to define local state, that gets set from the props? If the state is being updated inside ListBooks, then maybe you need to define it as state and call BooksAPI.getAll inside ListBooks... – Yossi Mar 22 '18 at 18:54
  • Think I get it now! Thanks – Robinj Mar 22 '18 at 19:15
  • I am glad! Good luck! – Yossi Mar 22 '18 at 20:32