React setState doesn't update - javascript

I saw thousands post about it so I am a bit confuse, I did use arrow function, I binded my method changed for componentDidUpdate but I still can't manage to
in my async call setState my data and pass it to my child component.
ParentComponent
class ParentComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
activeNav: 1,
loading: true,
data: []
};
this.fetchData = this.fetchData.bind(this);
}
fetchData = () => {
var that = this;
getMyData()
.then(res => {
console.log(res); // Output res Object
that.setState({
data: res
})
})
.catch((error) => {
console.log(error);
});
}
componentDidUpdate = () => this.fetchData()
render() {
const { data, loading } = this.state;
return (
<>
<ChildComponent data={this.data} loading={loading}/>
</>
);
}
}
ChildComponent
class CurrentUp extends React.Component {
constructor(props) {
super(props);
}
componentDidUpdate = () => {
console.log(this.props.data); // Output []
console.log(this.props.loading); // Output true
}
}
render() {
console.log(this.props.data); // Output []
console.log(this.props.loading); // Output true
return (
<div>
</div>
);
}
}
What am I missing ?
Solved, i am not sure how. I kept trying different stuff

You are console logging the data in the componentDidMount method. This method runs only once when a component is mounted. When you update the data in the parent component, and pass it to the child component, it doesn't re-create the entire component instance. Only the updates are passed down. You can access those updates in componentDidUpdate method. Or, if you are directly accessing props, you can log the data inside the render method.
class CurrentUp extends React.Component {
constructor(props) {
super(props);
}
render = () => {
console.log(this.props.data);
console.log(this.props.loading);
return null;
}
}
Other points:
You don't need to bind the function with this if you are using an arrow function.
Inside the arrow function, it is safe to use the this keyword. No need of assigning it to that.

Try to bring some more code cause I see the child component you posted is an instead current up component. Send the real child component and then move the var in your fetchData function to the constructor.

Related

React: How do I pass props to state component?

Need help passing state as a prop to another state component. I'm very new to React and I'm not sure what I'm doing wrong. When I console.log inside the Timer component it displays undefined but when I console.log in the Main component it displays the object perfectly.
class Main extends React.Component {
constructor() {
super()
this.state = {
isLoaded: false,
itemsP:{}
}
}
componentDidMount() {
fetch("https://api.spacexdata.com/v3/launches/next")
.then(response => response.json())
.then(
(resData) =>
this.setState({
isLoaded: true,
itemsP: resData
})
)
}
render() {
console.log(this.state.itemsP) //this will console.log the object from the api
return (
<main>
<Timer nextLaunch={this.state.itemsP} />
</main>
)
}
}
//Timer component
class Timer extends React.Component{
constructor(props) {
super(props)
this.state = {
nextDate: props.nextLaunch.launch_date_utc
}
}
render() {
console.log(this.state.nextDate) //will console log UNDEFINED why is this?
return (
<div>
//display something....
</div>
)
}
}
here is the link for the API that I'm using for reference.
#tlrmacl might have answered it there. It's missing the this keyword. In theory, you might be assigning the initial value still to the state.
It is due to how react lifecycle works
On your componentDidMount(), you are calling setState after the jsx gets mounted to the DOM. this.state.itemsP is given an initial value of {} then after the mount, it will receive its new value from comopnentDidMount()
Inside your Timer component, you are assigning the first value of this.props.nextLaunch to a new state. It doesn't have the chance to update the value. Instead of doing this:
this.state = {
nextDate: props.nextLaunch.launch_date_utc
}
use props.nextLaunch.launch_date_utc directly:
console.log(props.nextLaunch.launch_date_utc)
For more information check out this tweet by Dan Abramov here
From ur parent component u pass value as nextLaunch
Dont forget to call props in ur parent component constructor
constructor(props) {
super(props);
this.state = {
isLoaded: false,
itemsP: 'myValue'
}
}
<Timer nextLaunch={this.state.itemsP} />
So in your Timer Component to print ur value you have to call ur props this way this.props.nextLaunch
class Timer extends React.Component {
render() {
return <p>My nextLaunch data are {this.props.nextLaunch}.</p>;
}
}
Hope it helps
//Timer component
class Timer extends React.Component{
constructor(props) {
super(props)
this.state = {
nextDate: this.props.nextLaunch.launch_date_utc
}
}
You need this.props instead of props in your constructor

prop passed to component return undefined

I have asynchronous method get called and I need the value from it to be render on the first cycle so it will be passed to the next component that being render and I cant make id done. The component is rendered before the value is returned which cause the the prop be undefined when it passed.
Any idea how can I delay the rendering till the value get returned?
CODE SAMPLE:
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
valueHere:''
}
}
componentWillMount() {
axios.post(url, {
username:this.state.username
})
.then((res) => {
this.setState({
valueHere:res.data
})
})
}
render() {
return(
<AnotherComponent
someValue={this.state.valueHere}
/>
)
}
}
export default class AnotherComponent extends Component {
constructor(props) {
super(props);
}
render() {
console.log(this.props.someValue) // undefined
return(
<View/>
)
}
}
You can use conditional rendering.
So basically, you check if that value exists then render the component else return null/any other component;
render() {
return( this.props.someValue? <ActualComponeent /> : <div> some loader...</div>)
}

Rendering component with its state

I'm really confused now about lifecycle hooks. Here's my code:
App.js:
class App extends Component {
constructor(props){
super(props);
this.state = {
arrayOfComponents: []
}
}
componentDidMount(){
//i get the properties from the server which responds with the database's elements
fetch('http://localhost:3001/')
.then(res => res.json())
.then(arrayOfData => this.setState({arrayOfComponents: arrayOfData}))
.catch(err => console.log(err))
}
render() {
console.log(this.state) //first returns empty array, after the mount returns the db's array of elements
return (
<div className="App">
<Component name='First' id={1} componentsComponents={this.state.arrayOfComponents} />
</div>
);
}
}
Component.js:
class Component extends React.Component{
constructor(props){
super(props);
this.state={
componentsComponents: []
}
}
//here i was tried with componentDidMount, componentWillMount to set the
//this.props.componentsComponents to this.state.componentsComponents
//but it didn't work
renderComponents = () => {
if(this.state.componentsComponents.length){
return this.state.componentsComponents.filter(c => c.inhertedFromId === this.props.id).map(c => {
return <Component name={c.name} id={c.id} componentsComponents={this.props.componentsComponents} />
})
}
}
render(){
return(
<div>
{this.renderComponents()}
</div>
)
}
}
So what i want to do is to the components renders themselves, depending on the array they get from the App.js. But how to set the state before the render happens? Can i somehow ask the component to render again if it did mount? Or any other solutions?
You can simply assign this.props.componentsComponents in constructor itself only.
constructor(props){
super(props);
this.state={
componentsComponents: this.props.componentsComponents||[]
}
}
Bring Filter Up To App
Here it appears you are not calling renderComponents, and you are also trying to render a Component inside itself, which is difficult to reason about. Bring the renderComponents function up to App, and render the data using Component inside of App, and simply pass props down to a stateless Component, which may be a simpler solution to represent the data.
If a recursive call is indeed the best way to represent this data, may need to use getDerivedStateFromProps to move props into state on update, if you wish to store that data in state - something like:
static getDerivedStateFromProps(nextProps) {
return {
componentsComponents: nextProps.componentsComponents
}
}
Added to Component.

ReactJs: Passing url param to outside functions

I'm working on an Isomorphic react application using redux. I'm having issues passing a url parameter into a function that dispatches a redux action.
class SingleMovie extends Component {
componentDidMount(){
console.log(this.props.match.params.id); // i can access the id here
}
.....
}
function mapStateToProps(state){
return {movie:state.movie};
}
function loadData(store, id){ //<--how do i get the id parameter
return store.dispatch(fetchMovie(id)); //<--so i can pass it here
}
export default {
loadData,
component: connect(mapStateToProps,{fetchMovie})(SingleMovie)
};
What i tried:
(1) Declare a constant outside the React component
let id = "";
class SingleMovie extends Component {
...
(2) Try to assign the global variable with the id
componentDidMount(){
id= this.props.match.params.id;
}
The id always ends up being undefined.
First save the id params to the state, and then use setState's callback function to dispatch the fetchMovie action.
class SingleMovie extends Component {
constructor(){
this.state = {
id: ""
}
}
componentDidMount(){
//console.log(this.props.match.params.id);
this.getMovieId(this.props.match.params.id);
}
getMovieId = (movieId) => {
this.setState({
id: movieId
}, () => {
this.loadData();
})
}
loadData = (store) => {
store.dispatch(fetchMovie(this.state.id));
}
render(){
return(
...
)
}

How can I set initial React state from API data?

I have a rest API back end set up. I want to set values inside the getInitialState function for a component, but I can't figure out how to populate the object I need to return because I am using an asynchronous http request. The object I return has undefined values, as expected. How do I get around this?
I'm using fetch (can switch to any other library, honestly) as of now. I can't figure out how to call getInitialState after the asynchronous call has returned some value instead of before it happens.
import React from 'react';
import 'whatwg-fetch';
export default class IndexPage extends React.Component {
render() {
// I basically need to call getInitialState after the last promise has been resolved
fetch('https://localhost:3000/api/aye')
.then(function(response) {
return response.json();
})
.then(function(json) {
// Need to return some values from this.
});
return (
<div>
<h1>{this.state.jsonReturnedValue}</h1>
</div>
);
}
}
Thanks in advance!
You should call this.setState in order to change state values
export default class IndexPage extends React.Component {
constructor() {
super();
this.state = {
jsonReturnedValue: null
}
}
componentDidMount() {
fetch('https://localhost:3000/api/aye')
.then(response => response.json())
.then(json => {
this.setState({ jsonReturnedValue: json });
});
}
render() {
return (
<div>
<h1>{ this.state.jsonReturnedValue }</h1>
</div>
);
}
}
In your situation -
It's better to get the rendering done for the first time with empty state data lets say
constructor(props){
super(props);
this.state = {
data : []
};
}
and make ajax call in componentDidMount, this is the place where you can perform dom manipulation and send ajax request to fetch data via REST.
After new data is fetched from server set the state with new data
this.setState({data:newDataFromServer});
e.g In componentDidMount
componentDidMount() {
sendAjaxRequest()
.then(
(newDataFromServer) => {
this.setState({data : newDataFromServer });
});
}
This will cause the re-rendering to happen with latest data fetched from server and new state changes will be reflected.
You should do something along the following. Firstly, create the state in the constructor method. This will then allow you to use this value in your render method by referencing {this.state.jsonReturnedValue}. The constructor() method is the ES6 version of getInitialState() in case you are not aware of this. This is where you should set the state of the component.
Then in the componentDidMount which will run after the render method you can make the API call api and use React's setState method to update the value of this.state.jsonReturnedValue.
When this.setState() runs and sets a new value it will then run the render method again and update the view accordingly.
export default class IndexPage extends React.Component {
constructor(){
super();
this.state = {
jsonReturnedValue: null
}
}
componentDidMount() {
this.jsonList();
}
jsonList() {
fetch('https://localhost:3000/api/aye')
.then(function(response) {
this.setState({
jsonReturnedValue: response.json()
})
})
}
render() {
return (
<div>
<h1>{this.state.jsonReturnedValue}</h1>
</div>
);
}
}

Categories

Resources