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.
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 :)
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.
Related
I have a base component where i want to have an instance level variable that is updated by subclasses. The only way i can get this work is to add it to state which i don't want to do due to the fact that the value of this variable is NOT related to the UI so i don't want to trigger a re-render.
So take following component:
class BaseComponent extends React.Component {
constructor(props) {
super(props);
this.params = [];
}
addParam(name) {
this.params.push(name);
}
getUrl() {//this.params is always undefined
return format("{0}?", this.constructor.name, this.params.join("&");
}
}
So this.params is always undefined which makes sense since it is not part of state. This variable is related to the url and not the UI so when the value is updated i don't want to have to trigger a re-render by updating the state.
Not sure what the best approach here is?
Options i know of:
1) Add to state which will trigger re-render
2) Call forceUpdate() which will also trigger re-render
I don't like either of these options so i am hoping someone knows the correct way to do what i am trying to do.
my English is not good but I try, to explain my problem.
I am new in react, I need to know how can I change local component state with global component if conditional is met..
It is difficult to answer because you have no code in your question. You will find you will garner a better response by providing what you have tried. However I will guess that what you need to do is setState according to a condition that you define.
setState(updater[, callback])
From the docs:
setState() enqueues changes to the component state and tells React that this component and its children need to be re-rendered with the updated state. This is the primary method you use to update the user interface in response to event handlers and server responses.
For example in psuedo code:
if (predicate) {
this.setState((prevState, props) => {
return {key: value};
});
}
You can start by reading the docs here for more on setState.
I'm working with react-redux in a current project and on my search for react wisdom I came across a 'convention' I don't understand.
Other programmers tend to put a prop to state right in the constructor. Why is that?
I've never seen it in the official documentation and from there I learned that these two are two different things to hold and share data.
Is there any use in it or mybe just a personal preference?
Thanks.
It sounds like a pattern for when you need some initial value from outside the component, but then want to either ignore, or not immediately affect, the outside (by for example dispatching Redux actions on each value change).
For example, maybe you have a form that should be prefilled with some values that the component gets from the outside (Redux state) through mapStateToProps or similar. You could let every form fields onChange dispatch an action that changes the Redux state, causes the incoming props to change, and then re-render the form with the new value. Or maybe you find that to be overkill. Maybe you're satisfied with keeping the changing form data in the component internal state until the user actually submits the form, and only then dispatch an action that will post the form data, change the Redux state and pass new props (like success status) down to the form component.
Yes, it is possible, especially when you want to keep things simple by not using container components or flux / redux store to manage application's state.
A component can manage its own state, and the initialState will be assigned as the props passed from its parent component.
Consider the following example:
class TodoList extends React.Component {
constructor(props) {
// Assign todos property to state so that the TodoList component
// can self-manage this value without interacting with outside components.
this.setState({ todos: props.todos });
}
...
addTodo(todoDescription) {
this.setState({ todos: this.state.todos.concat[todoDescription] });
}
}
However, I still do recommend to separate the view components and data manipulating components when your applications is complex.
Thanks for reading. I know this is going to seem over-simplified, but I am really stumped as to why this behavior is happening. I am calling an API using axios, getting the answer back, and passing that answer to the component below via props. When I let the following code execute, the console shows the object - populated with the server/api data.
class Player extends React.Component{
constructor(props) {
super(props);
}
render() {
console.log(this.props.personData)
return ...
}
}
If I add a field: console.log(this.props.personData.first_name) I get an error that personData is null, can't access its members. Thought that was weird, so stepped back to test and stumped myself further:
If I set a breakpoint in Chrome at the console.log line and instead type this.props.personData into console I get null. If I then step over the console.log line, it outputs null. But if I remove the breakpoint, that same line of code outputs a data-populated object.
Can anyone explain this behavior?
Assuming your component is rendered regardless of if Axios has returned yet, the personData prop will be null (or whatever you construct it in the parent's state) until Axios returns the async call.
You can either conditionally render the Player component once the data returns, or don't reference attributes of the data until you know it's not null.
This is because the Player component will be rendered initially when the parent mounts, however, the Axios API call will be async so the data isn't available.
Also, if the Player component is rendering attributes of personData without checking that they exist, it will throw and error and break the rendering cycle.
As to your question about breakpoint / logging differences, it's because the console log is mapped to the memory location of the prop, so eventually it is set and will display in the log. But, if you check by a breakpoint when you initially render it, the value will be null, and it will throw the above mentioned error.
HOw you are passing data to the component using redux or flux.
A story:
I have a server rendering but some part of my app cannot be done there, since they use document(react-art library for drawing on a canvas).
I cannot render everything in the same from, because react will say that the code received from a server and and client one are not the same.
So my solution is to render some part of my app on the server, then render this part on a client and, in the next frame, render everything that is impossible to render on a client.
So I was thinking about using setState in componentDidMount and trigger DOM update, so it can contain rendered client part which cannot be rendered on a server, but eslint says it is not good to set state in componentDidMount. Logically I cannot say why it is bad in this case. Generally it is not good because it triggers DOM update, but in my case, this is what I actually need.
What would you suggested in this case?
Edit 1:
Fixed typo, I mean componentDidMount not componentDidUpdate.
Edit 2:
Here is the same issue, but they use changing state in componentDidMount as a workaround.
I've just coded a similar scenario and happily used componentDidMount disabling ESLint's warning. IMHO it is perfectly valid ONLY in universal rendering scenarios.
Why: Using setState in discouraged in componentDidMount because you'll trigger an additional, unnecessary render.
However in a universal scenario where you want to differ server and client behaviour, you'll want that additional render and in order to prevent 'could not reuse markup' errors, you'll want it just after react reconciles with the server rendered DOM. In brief: You'll render twice in any case.
I don't however recommend coding this behaviour in a leaf / dumb component because doing so would require the supposedly dumb component to have some knowledge of it's environment (client / server) and present a design problem. Creating a component that would set the props of the dumb component would be the obvious solution.
Try setting state in componentWillMount or componentWillUpdate.
Be careful with componentWillMount, it can also be called server side.
What i understand from your question is that after your component is mounted, data on server and client changes and you want to keep component in sync with changing data.
I recommend you have a look at the flux or redux architectures of react. For example flux is implemented in a way that when anything in component changes it triggers action and listens to the store. And when anything in store is changed then component will re render itself.
Please see this link About ReactJS - Component Life Cycle.
you should use from componentWillMount cycle. componentWillMount is executed before rendering, on both server and client side.
I want to know how to trigger components update after my app is mounted.
In React, the conventional way to "push" state onto a component is using props, not state. If your application needs to acquire information client-side and adjust component state accordingly, you should implement componentWillReceiveProps.
If you feel that the state rendered on the server and in-browser are of the same kind, and don't want the hassle of tracking which properties belong this.state v. this.props, I suggest something like the following:
componentWillReceiveProps(nextProps) {
this.setState( Object.assign({}, this.state, nextProps) );
}
This will merge the passed properties into the component state, so that they can be accessed using this.state.
Prevent usage of setState in componentDidMount.
Updating the state after a component mount will trigger a second render() call and can lead to property/layout thrashing.
The following patterns are considered warnings:
class MyComponent extends React.Component {
componentDidMount(){
this.setState({
data: data
});
}
}
The following patterns are not considered warnings:
class MyComponent extends React.Component {
constructor(){
this.setState({
data: data
});
}
componentDidMount(){
...
}
}