ReactJS beginner state updated but rendering in child not - javascript

Bear with me beginner at ReactJS so I am testing stuff out.
I created a static page as a component and in that component I load another custom component.
The data is coming from an ajax call and I update the state but it doesn't update the child component's view.
The static page
// Test.JS
import React from 'react';
import Layout from '../../components/Layout';
import Article from '../../components/Article';
import s from './styles.css';
import axios from 'axios';
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
article: null
};
}
componentWillMount(){
axios.get("https://gist.githubusercontent.com/koistya/a32919e847531320675764e7308b796a/raw/articles.json")
.then(function(result) {
var theArticle = result.data.filter((article) => article.title.split(' ').join('') === this.props.route.params.title.split(' ').join(''));
this.setState({
article: theArticle[0]
})
}.bind(this));
}
render() {
return (
<Layout className={s.content}>
<Article {...this.state.article} />
</Layout>
);
}
}
export default Test;
As you can see i update the state with an ajax call but the child is not updated. the console log in Article is showing a null object because first time it renders there is nothing inside. But after updating the state I expect it should pass it through to the childeren.
import React from 'react';
import Link from '../Link';
class Article extends React.Component{
componentDidMount() {
console.log(this.props);
window.componentHandler.upgradeElement(this.root);
}
componentWillUnmount() {
window.componentHandler.downgradeElements(this.root);
}
render() {
return (
<div className="demo-card-wide mdl-card mdl-shadow--2dp" ref={node => (this.root = node)}>
<div className="mdl-card__title">
<h2 className="mdl-card__title-text">{this.props.author}</h2>
</div>
<div className="mdl-card__supporting-text">
</div>
<div className="mdl-card__actions mdl-card--border">
</div>
<div className="mdl-card__menu">
<button className="mdl-button mdl-button--icon mdl-js-button mdl-js-ripple-effect">
<i className="material-icons">{this.props.title}</i>
</button>
</div>
</div>
);
}
}
export default Article;
So I have the following questions:
1) First of all am I going the right direction is this how it should be done ?
2) Is a constructer not better than willmount event ?
2) Why is it now not updating the child view ?
3) Should I use a prop or state in this case (in Test.js) (still not sure after reading allot about it)

I changed it to componentDidMount and now it updates the view.
I have no idea why it didnt work before!

Related

React props are updating even when i'm not using the props

I made a project using the create-react-app template. I am trying to import data from a JSON file and send it to the todos component file as props but I'm not using it as a prop in the Todos file but when I update the file using add button in-app component, it updates the todo list. I don't understand why it is doing that. It will be a great help if someone can explain what's going on.
This is the app.js file
import { Component } from 'react';
import Todos from "./todos";
import tasks from './data.json';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = { task: '' };
this.addTask = this.addTask.bind(this);
this.handleChange = this.handleChange.bind(this);
}
addTask() {
tasks.push({
title: this.state.task,
done: false
});
this.setState({ task: "" });
}
handleChange(event) {
this.setState({ task: event.target.value });
}
render() {
return (
<div className="App">
<header className="App-header">
<input className="App-task-input" type="text" placeholder="Title"
value={this.state.task} onChange={this.handleChange} />
<button className="App-add-btn" onClick={this.addTask}>Add Task</button>
</header>
<Todos tasks={tasks} />
</div>
);
}
}
export default App;
This is todos.js file
import { Component } from "react";
import tasks from "./data.json";
class Todos extends Component {
changeCheckbox(index) {
console.log(index, tasks);
}
render() {
return (
<main className="todo">
// It should update if i use this.props.tasks instead of tasks
{tasks.map((t, i) =>
<div className="todo-item" key={i}>
<input
id={t.id}
className="todo-checkbox"
type="checkbox"
checked={t.done}
onChange={this.changeCheckbox.bind(this, i)} />
<label className="todo-label" htmlFor={t.id}>{t.title}</label>
</div>
)}
</main>
);
}
}
export default Todos;
Whenever state change component re-renders and here you have parent and child component.
In your parent component you are updating your lists that's why child also getting re-render whether you are passing changes or not. I will happen.
To prevent this, you need to use shouldComponentUpdate, React.memo or PureComponent.
For your reference to understand scenario and with example.
Whenever you call method setState(), component automatically re-renders
How to prevent unnecassery re-rendering

React Loading New Page

I am trying to load a different React component using a button. It worked when doing it for authentication with GitHub using Firebase, but won't work for this page.
import React from 'react';
import './index.css';
import GamePage from '../Game';
class Home extends React.Component {
constructor(props){
super(props);
this.LoadGamePage = this.LoadGamePage.bind(this);
}
LoadGamePage() {
return(
<div>
<GamePage />
</div>
)
}
render(){
return(
<div className="home">
<h1>Home Page</h1>
<button onClick={this.LoadGamePage}>Play PIT</button>
</div>
)
}
}
export default Home;
Is there something wrong with my LoadGamePage function?
How it is supposed to work? You have an onclick handler, which calls a class method. That class method, called LoadGamePage, returns JSX. Okey, but what now? It is returned, but... not rendered. It won't display anywhere. What would I suggest you? Instead of returning the JSX inside that handler, I would set state and depending on state I would render the Game Page or not.
class Home extends React.Component {
constructor(props){
super(props);
this.state = {
gameVisible: false,
}
this.LoadGamePage = this.LoadGamePage.bind(this);
}
LoadGamePage() {
this.setState({ gameVisible: true });
}
render() {
if (this.state.gameVisible) {
return <GamePage />
}
return (
<div className="home">
<h1>Home Page</h1>
<button onClick={this.LoadGamePage}>Play PIT</button>
</div>
)
}
}

React component re-renders endlessly with onClick

I want the onClick event of the button in result.js to render my Spinner component, and have so far (kind of) gotten it to do so. At the moment, Spinner mostly has some console.log() statements, and it keeps logging "Rendered spinner." endlessly after clicking the button, about once every second.
For the record, the returned paragraph isn't being displayed, but I haven't gotten around to debugging that yet. Also, I have excluded some code in Result.js that I think is irrelevant.
For now, I just want Spinner to only render once after pressing the button. Any tips?
result.js:
import React, { Component } from "react";
import { connect } from "react-redux";
import Spinner from "./spinner";
class UnboxResult extends Component {
constructor(props) {
super(props);
this.state = {
showSpinner: false
};
this.handleUnboxClicked = this.handleUnboxClicked.bind(this);
}
handleUnboxClicked(event) {
event.preventDefault();
console.log("Inside handleUnboxClicked");
this.setState({
showSpinner: true
});
}
render() {
return (
<section className="opening">
<div className="container">
<div className="row">
<button onClick={this.handleUnboxClicked}>UNBOX</button>
</div>
<div className="row">
{this.state.showSpinner ?
<Spinner items={this.props.unbox.items}/> :
null}
</div>
</div>
</section>
);
}
}
export default connect(state => ({
unbox: state.unbox
}))(UnboxResult);
spinner.js:
import React, { Component } from 'react';
class Spinner extends Component {
constructor(props) {
console.log("Before super");
super(props);
console.log("Ran constructor.");
}
render(){
console.log("Rendered spinner.");
return(
<p>Spinning..</p>
);
}
}
export default Spinner;
You could add a handler method to update the state from spinner
handleClick(){
this.setState({
showSpinner: true
})
}
and in your render it will need to be passed as prop
<div className="row">
{this.state.showSpinner ?
<Spinner handleClick={this.handleClick}/> :
null}
</div>
In your spinner component return you can trigger this using onclick
<button onClick = {this.props.handleClick} > Click </button>
This will allow you to update the state back in your parent, You might want to figure out how you would display the items one at a time in spinner and only set state to false when there is no items left to display.
Sorry if i misunderstood your comment.

React setState odd behaviour

New to React, in the following code I am passing data between two components via the parent. Flow is from Search to the parent App then to another child Sidebar. I am able to send to both from Search to App and App to Sidebar individually but for some reason setState is not behaving as expected making the link to trigger a refresh of <Search updateMenu={this.handleSearchResult} /> as you can see in the console.log code comments below:
import React, { Component } from 'react';
import './App.css';
import Search from './Search';
import Sidebar from './Sidebar';
class App extends Component {
constructor() {
super()
this.state = {
menu: []
}
}
handleSearchResult = (array) => {
// always the correct value
console.log('in ', array);
this.setState( {menu: menuList})
// 1st call : empty
// 2nd call : previous value not showing on 1st call + new value as expected
// does not trigger <Sidebar list={this.state.menu}/>
console.log('out', this.state.menu);
}
render() {
return (
<div className="App">
// not refreshing
<Search updateMenu={this.handleSearchResult} />
<Sidebar list={this.state.menu}/>
</div>
);
}
}
export default App;
Logging this.setState(). Is not so straight forward. this.setState() is asynchronus.
Here is a reference on Medium.

React with redux and component vs. container dilemma

I want to organize my code into components and containers folder structure. I will be using Redux with actions and reducers.
What do you think that StartButton is rather component or container? It will not be connected to the redux store, but it has its own state and some decision logic, so maybe it isn't so dumb...
I know my question has something to do with opinions, but perhaps someone can provide me with some insights and what's regarded as best practice.
Here's my StartButton component:
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import './style.css';
class StartButton extends Component {
constructor() {
super();
this.state = {
startWasClicked: false,
};
}
handleStartButton = () => {
this.setState({ startWasClicked: true });
};
beerListingView() {
if (this.state.startWasClicked) {
return <div>YES! It was clicked!</div>;
}
// Else return just single <div />
return <div />;
}
render() {
return (
<div>
<div className="StartButton-container">
<RaisedButton
label="Start Here"
className="StartButton-main"
onClick={this.handleStartButton}
/>
</div>
{this.beerListingView()}
</div>
);
}
}
export default StartButton;
In my apps, containers refer to react components that are connected to the Redux store.
components are all the others. Most of them make use of the React state to toggle UI stuff for example. Thats perfectly fine.
Check out https://github.com/react-boilerplate/react-boilerplate for example

Categories