Logging of State of the Class Component - javascript

I am setting the state to an empty array, then calling a function to update the state of the component. When I log the state of the function, I am getting the empty array and the updated array at the same time.
Any reason why this is happening and how to only log the updated state.
Following is my code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
videos: [],
};
YTSearch({ key: API_Keys, term: 'USA Top40' }, (videos) => {
this.setState({ videos });
});
}
render() {
return (
<div>
<SearchBar />
{console.log(this.state)}
</div>
)
}
}
Here is the console.log

You shouldn't return console.log in render method!
It's better your code should be:
class App extends Component {
constructor(props) {
super(props);
this.state = { videos: [] };
}
componentDidMount() {
YTSearch({ key: API_Keys, term: 'USA Top40' }, (videos) => {
this.setState({ videos });
});
}
componentDidUpdate(prevProps, prevState) {
const { videos } = this.state;
if(prevState.videos !== videos) {
console.log(videos);
}
}
render() {
return (
<div>
<SearchBar />
</div>
)
}
}
Also, you can place console.log outside of return:
render() {
console.log(this.state);
return (
<div>
<SearchBar />
</div>
)
}

Note: setState({}) is an async process more like a batch process that
runs asynchronously and doesn't block your UI. So you might not see
the updated results in the console right after you change the state.
I agree with #souroush's answer as it is recommended way of doing things. But one way to achieve what you're trying to do is make a function and console logging the state into it
logState = () =>{
console.log("State" , this.state)
}
render() {
return (
<div>
<SearchBar />
{this.logState()}
</div>
)}

Related

I'm unable to change the array of an existing state

I'm trying to change one value inside a nested state.
I have a state called toDoItems that is filled with data with componentDidMount
The issue is that changing the values work and I can check that with a console.log but when I go to setState and then console.log the values again it doesn't seem like anything has changed?
This is all of the code right now
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
toDoItems: null,
currentView: "AllGroup"
};
}
componentDidMount = () => {
fetch("/data.json")
.then(items => items.json())
.then(data => {
this.setState({
toDoItems: [...data],
});
})
};
changeToDoItemValue = (givenID, givenKey, givenValue) => {
console.log(this.state.toDoItems);
let newToDoItems = [...this.state.toDoItems];
let newToDoItem = { ...newToDoItems[givenID - 1] };
newToDoItem.completedAt = givenValue;
newToDoItems[givenID - 1] = newToDoItem;
console.log(newToDoItems);
this.setState({
toDoItems: {newToDoItems},
})
console.log(this.state.toDoItems);
};
render() {
if (this.state.toDoItems) {
// console.log(this.state.toDoItems[5 - 1]);
return (
<div>
{
this.state.currentView === "AllGroup" ?
<AllGroupView changeToDoItemValue={this.changeToDoItemValue}/> :
<SpecificGroupView />
}
</div>
)
}
return (null)
};
}
class AllGroupView extends Component {
render() {
return (
<div>
<h1 onClick={() => this.props.changeToDoItemValue(1 , "123", "NOW")}>Things To Do</h1>
<ul className="custom-bullet arrow">
</ul>
</div>
)
}
}
So with my console.log I can see this happening
console.log(this.state.toDoItems);
and then with console.log(newToDoItems)
and then again with console.log(this.state.toDoitems) after setState
State update in React is asynchronous, so you should not expect updated values in the next statement itself. Instead you can try something like(logging updated state in setState callback):
this.setState({
toDoItems: {newToDoItems},// also i doubt this statement as well, shouldn't it be like: toDoItems: newToDoItems ?
},()=>{
//callback from state update
console.log(this.state.toDoItems);
})

How to get props in compononentDidMount() of child component

I am trying to pass a parameter from the parent component to the child's component's ComponentDidMount() method. I am only able to receive the props inside the render() of the child component and I am not able to pass it to the
ComponentDidMount() method.
Parent Component - Provider.js
export default class Provider extends Component {
constructor(props) {
super(props);
this.state = {
carePlan: "",
patID: ""
};
}
async componentDidMount() {
this.setState({
carePlan: this.cp
patID: this.props.location.state.id
});
console.log(this.state.patID);
}
render() {
return (
<div>
<Layout>
{!this.state.cp ? (
<AddCarePlan patID={this.state.patID} />
) : (
<div className="carePlan">
<DisplayCarePlan cp={this.state.carePlan} />
</div>
)}
</Content>
</Layout>
</div>
);
}
}
Child Component - AddCarePlan.js
class AddCarePlan extends Component {
constructor(props) {
super(props);
}
async componentDidMount() {
const patientID = this.props.patID;
console.log(patientID) // does not show ID
}
render() {
const patientID = this.props.patID;
console.log(patientID) // shows ID
return (
<div>
<h1> Add Care Plan </h1>
</div>
);
}
}
export default AddCarePlan;
You should remove keyword async before lifecycle methods in your components. As I can tell you are not using await nowhere inside of them and + React is not design to use them with async await functions. Even if you want to use componentDidMount to do some data fetching you should not use async since when data arrives, then() method on data fetching wil trigger component rerendering.
Try removing async from your code.
what about try to this?
{!this.state.cp ? (
this.state.patID ? <AddCarePlan patID={this.state.patID} /> : ''
) : (
<div className="carePlan">
<DisplayCarePlan cp={this.state.carePlan} />
</div>
)}
export default class Provider extends Component {
constructor(props) {
super(props);
this.state = {
carePlan: "",
patID: props.location.state.id
};
}
async componentDidMount() {
this.setState({
carePlan: this.cp
});
console.log(this.state.patID);
}
render() {
return (
<div>
<Layout>
{!this.state.cp ? (
<AddCarePlan patID={this.state.patID} />
) : (
<div className="carePlan">
<DisplayCarePlan cp={this.state.carePlan} />
</div>
)}
</Content>
</Layout>
</div>
);
}
}
try this way
this.state = {
carePlan: "",
patID: ""
};
async componentWillMount() {
this.setState({
patID: this.props.location.state.id
});
}
or try changing lifecycle

Why my functions is not returning a component? React

I have a function that render LoginPage if the user is not logged and render the IndexPage if is logged, but It is not rendering none, I tried alerting the user.displayName and It work. See my code.
renderPage = () => {
firebase.auth().onAuthStateChanged(user => {
if (user) {
return <IndexPage />;
} else {
return <LoginPage />;
}
});
};
render() {
return <div>{this.renderPage()}</div>;
}
Why is not working?
You miss a return in the renderPage function, but performing async requests in render is not a good approach in react.
What you should do, is to move the user into the state, then on componentDidMount fetch the user from your async code, and inside your render use the state prop user.
So your code should be something like:
constructor() {
this.state = { user: null };
}
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
user ? this.setState({ user }) : this.setState({ user: null });
});
}
render() {
const content = this.state.user ? <IndexPage /> : <LoginPage />;
return <div>{content}</div>;
}
Your function inside render method is async function, what you get is undefined.
You should store the user state. Do something like,
class YourComponent extends Component {
constructor(props) {
super(props);
this.state = {
user: null
};
}
componentDidMount() {
firebase.auth().onAuthStateChanged(user => {
if (user) {
this.setState({
user
});
}
});
}
render() {
return (
{this.state.user ? <IndexPage /> : <LoginPage />}
);
}
}

reactjs -- Solving setState async problem

I've read this post: React setState not Updating Immediately
and realized that setState is async and may require a second arg as a function to deal with the new state.
Now I have a checkbox
class CheckBox extends Component {
constructor() {
super();
this.state = {
isChecked: false,
checkedList: []
};
this.handleChecked = this.handleChecked.bind(this);
}
handleChecked () {
this.setState({isChecked: !this.state.isChecked}, this.props.handler(this.props.txt));
}
render () {
return (
<div>
<input type="checkbox" onChange={this.handleChecked} />
{` ${this.props.txt}`}
</div>
)
}
}
And is being used by another app
class AppList extends Component {
constructor() {
super();
this.state = {
checked: [],
apps: []
};
this.handleChecked = this.handleChecked.bind(this);
this.handleDeleteKey = this.handleDeleteKey.bind(this);
}
handleChecked(client_id) {
if (!this.state.checked.includes(client_id)) {
let new_apps = this.state.apps;
if (new_apps.includes(client_id)) {
new_apps = new_apps.filter(m => {
return (m !== client_id);
});
} else {
new_apps.push(client_id);
}
console.log('new apps', new_apps);
this.setState({apps: new_apps});
// this.setState({checked: [...checked_key, client_id]});
console.log(this.state);
}
}
render () {
const apps = this.props.apps.map((app) =>
<CheckBox key={app.client_id} txt={app.client_id} handler={this.handleChecked}/>
);
return (
<div>
<h4>Client Key List:</h4>
{this.props.apps.length > 0 ? <ul>{apps}</ul> : <p>No Key</p>}
</div>
);
}
}
So every time the checkbox status changes, I update the this.state.apps in AppList
when I console.log new_apps, everything works accordingly, but console.log(this.state) shows that the state is not updated immediately, which is expected. What I need to know is how I can ensure the state is updated when I need to do further actions (like register all these selected strings or something)
setState enables you to make a callback function after you set the state so you can get the real state
this.setState({stateYouWant}, () => console.log(this.state.stateYouWant))
in your case:
this.setState({apps: new_apps}, () => console.log(this.state))
The others have the right answer regarding the setState callback, but I would also suggest making CheckBox stateless and pass isChecked from MyApp as a prop. This way you're only keeping one record of whether the item is checked, and don't need to synchronise between the two.
Actually there shouldn't be two states keeping the same thing. Instead, the checkbox should be stateless, the state should only be kept at the AppList and then passed down:
const CheckBox = ({ text, checked, onChange }) =>
(<span><input type="checkbox" checked={checked} onChange={() => onChange(text)} />{text}</span>);
class AppList extends React.Component {
constructor() {
super();
this.state = {
apps: [
{name: "One", checked: false },
{ name: "Two", checked: false }
],
};
}
onChange(app) {
this.setState(
previous => ({
apps: previous.apps.map(({ name, checked }) => ({ name, checked: checked !== (name === app) })),
}),
() => console.log(this.state)
);
}
render() {
return <div>
{this.state.apps.map(({ name, checked }) => (<CheckBox text={name} checked={checked} onChange={this.onChange.bind(this)} />))}
</div>;
}
}
ReactDOM.render(<AppList />, document.body);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>

How to Set State with arguments passed to a function in React

I'm trying to pass an array (titles) from a child component to the parent, then set the state of the parent with the array. However, when handling the change in the increaseReads() method, I cannot change the articlesRead state
You will see two console.log() statements; the first one is successfully logging the titles but the second is logging an empty array - the previous state
The Child:
export class Publication extends React.Component {
constructor() {
super();
this.state = {
items: []
};
}
componentDidMount() {
fetch(this.props.url)
.then(response => {
return response.json();
}).then(({ items })=> {
this.setState({ items });
});
}
handleClick () => {
this.props.openArticle();
}
render() {
return (
<div className='publication'>
<h4>{this.props.name}</h4>
<ul>
{this.state.items.map(item => (
<li><a href={item.link} target='_blank' onClick={this.handleClick}>{item.title}</a></li>
))}
</ul>
</div>
);
}
}
The Parent:
export class Latest extends React.Component {
constructor(props) {
super(props);
this.state = {
totalReads: 0,
articlesRead: []
};
}
handleChange = () => {
this.props.increaseTotal();
}
increaseReads(titles) {
this.setState({
totalReads: this.state.totalReads + 1,
articlesRead: titles
})
// Won't log correctly
console.log(this.state.articlesRead);
this.handleChange();
}
render() {
return (
<div className='container'>
<Publication total={(titles) => {this.increaseReads(titles)}} name='Free Code Camp' api={'https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.freecodecamp.org%2Ffeed%2F'}/>
<Publication total={() => {this.increaseReads()}} name='Code Burst' api={'https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fcodeburst.io%2Ffeed%2F'}/>
<Publication total={() => {this.increaseReads()}} name='JavaScript Scene' api={'https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fmedium.com%2Ffeed%2Fjavascript-scene%2F'}/>
<Publication total={() => {this.increaseReads()}} name='Hacker Noon' api={'https://api.rss2json.com/v1/api.json?rss_url=https%3A%2F%2Fhackernoon.com%2Ffeed'}/>
</div>
)
}
}
I'm sure it is something small, but any help would be greatly appreciated!
The issue might be that you are expecting this.setState to be synchronous. See the documentation here.
Take a look at this CodeSandbox demo. this.setState accepts a callback as the second argument. This callback is invoked after this.setState has completed.
Notice how in the console.log output, we can see the old and new state values.

Categories