State is not getting updated - javascript

I'm currently using Flickr api to make a Simple Image Carousel and facing a problem where my state does not get updated or rendered whenever I click the button.
Here is my index.js
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import axios from 'axios';
import _ from 'lodash';
import Photo from './components/photo';
const urlArr = [];
const apiKey = "API";
const userId = "ID";
const url = `https://api.flickr.com/services/rest/?method=flickr.people.getPublicPhotos&api_key=${apiKey}&user_id=${userId}&format=json&nojsoncallback=1`;
class App extends Component {
constructor(props) {
super(props);
this.state = { urlArr: [] };
axios.get(url)
.then((photoData) => {
_.forEach(photoData.data.photos.photo, (photo) => {
urlArr.push(`https://farm6.staticflickr.com//${photo.server}//${photo.id}_${photo.secret}_z.jpg`);
});
this.setState({ urlArr });
});
}
render() {
return (
<div>
<Photo urls={this.state.urlArr}/>
</div>
);
}
};
ReactDOM.render(<App/>, document.querySelector('.container'));
and here is my photo.js
import React, { Component } from 'react';
import NextButton from './nextButton';
import PrevButton from './prevButton';
class Photo extends Component {
constructor(props) {
super(props);
this.state = { idx: 0 };
this.nextImg = this.nextImg.bind(this);
}
nextImg() {
this.setState({ idx: this.state.idx++ });
}
render() {
if (this.props.urls.length === 0) {
return <div>Image Loading...</div>
}
console.log(this.state);
return(
<div>
<PrevButton />
<img src={this.props.urls[this.state.idx]}/>
<NextButton onClick={this.nextImg}/>
</div>
);
}
}
export default Photo;
and my nextButton.js (same as prevButton.js)
import React from 'react';
const NextButton = () =>{
return (
<div>
<button>next</button>
</div>
);
};
export default NextButton;
Since I'm fairly new to React, I'm not quite sure why my this.state.idx is not getting updated when I click on the next button (Seems to me that it is not even firing nextImg function either). If anyone can give me a help or advice, that would really appreciated.
Thanks in advance!!

Update your NextButton. Use the event within your presentational component.
<NextButton next={this.nextImg}/>
And the NextButton component should looks like this.
import React from 'react';
const NextButton = (props) =>{
return (<div>
<button onClick={props.next}>next</button>
</div>
);
};

The problem lies with this piece of code:
axios.get(url)
.then((photoData) => {
_.forEach(photoData.data.photos.photo, (photo) => {
urlArr.push(`https://farm6.staticflickr.com//${photo.server}//${photo.id}_${photo.secret}_z.jpg`);
});
this.setState({ urlArr });
});
this refers to the axios.get callback scope and not the Component. You can define another variable called self or something that makes more sense to you and call self.setState().
See this question for a similar problem: Javascript "this" scope

Related

Text area not rendering React

I'm not sure why my text area isn't rendering if someone could suggest what I'm missing or what I've done wrong I'd be forever grateful.
I'm not sure whether maybe I should try rending it in the App.js file.
I'm also sure I don't need to import the App.js file to the CardCheck.js because it's a child.
import "./App.css";
import React from "react";
import CardCheck from "./CardCheck";
class App extends React.Component() {
state = {
cardNumber: "",
};
handleChange = (event) => {
this.setState({ cardNumber: event.target.value });
};
handleClick = () => {
const { cardNumber } = this.state;
this.setState({
cardNumber: "",
});
};
render() {
const { cardNumber } = this.state;
return (
<div className="App">
<h1>Taken Yo Money</h1>
<CardCheck
cardNumber={cardNumber}
handleChange={this.handleChange}
handleClick={this.handleClick}
/>
</div>
);
}
}
export default App;
function CardCheck(props) {
const { cardNumber, handleChange, handleClick } = props;
return (
<div className="TweetInput">
<div className="bar-wrapper"></div>
<textarea onChange={handleChange} value={cardNumber}></textarea>
<footer>
<button onClick={handleClick}>Enter Card Details</button>
</footer>
</div>
);
}
export default CardCheck;
Remove the parentheses from class App extends React.Component(). It should be
class App extends React.Component {
//rest of the code
}
``
You need to replace class App extends React.Component() with class App extends Component {
The Component is imported from import React, { Component } from "react";
It should fix the rendering issue

How do I make a client-side only component for GatsbyJS?

How do I create a component for Gatsby that will load on the client-side, not at build time?
I created this one and it renders with gatsby develop but not with the rendered server-side rendering
import React from 'react';
import axios from 'axios';
import adapter from 'axios-jsonp';
export default class Reputation extends React.Component<{}, { reputation?: number }> {
constructor(props) {
super(props);
this.state = {};
}
async componentDidMount() {
const response = await axios({
url: 'https://api.stackexchange.com/2.2/users/23528?&site=stackoverflow',
adapter
});
if (response.status === 200) {
const userDetails = response.data.items[0];
const reputation = userDetails.reputation;
this.setState({
reputation
});
}
}
render() {
return <span>{ this.state.reputation?.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") }</span>
}
}
If you don't want the component to be bundled in the main js file at build time, use loadable-components
Install loadable-components and use it as a wrapper for a component that wants to use a client-side only package. docs
import React, { Component } from "react";
import Loadable from "#loadable/component";
const LoadableReputation = Loadable(() =>
import("../components/Reputation")
);
const Parent = () => {
return (
<div>
<LoadableReputation />
</div>
);
};
export default Parent;
before render this component, make sure you have a window, to detect if there is a window object. I would write a hook for that:
function hasWindow() {
const [isWindow, setIsWindow] = React.useState(false);
React.useEffect(() => {
setIsWindow(true);
return ()=> setIsWindow(false);
}, []);
return isWindow;
}
In the parent component check if there is a window object:
function Parent(){
const isWindow = hasWindow();
if(isWindow){
return <Reputation />;
}
return null;
}

Embedded child component doesn't get changed when I click a new one with React Router

I started to use React Router earlier today for the first time, and I would like to embed an article's view inside the article's index. This works, the only problem is that when I want to change articles, the url changes, I get informations from the new one too, but nothing changes visually.
Here is my code from both the parent component and the child component:
articles.js
import React from 'react';
import utils from '../lib/functionsLibrary';
import Article from './article';
import {Link, Route} from "react-router-dom";
export default class Articles extends React.Component {
constructor(props) {
super(props);
this.state = {articles: null};
}
componentDidMount() {
let self = this;
utils.loader(window.location.origin + '/all_articles.json', function (articles) {
self.setState({articles: articles})
});
}
render() {
const {articles} = this.state;
const { match, settings } = this.props;
return (
<div>
<ul>
{
articles !== null &&
articles.map(function (a, i) {
return (
<li key={i}>
<Link to={`/articles/${a.slug}`}>{a.title}</Link>
</li>
)
})
}
</ul>
<Route path={`/articles/:slug`} component={Article} settings={settings}/>
<Route
exact
path={match.url}
render={() => <h3>Please select a topic.</h3>}
/>
</div>
)
}
}
article.js
import React from 'react';
import utils from '../lib/functionsLibrary';
export default class Article extends React.Component {
constructor(props) {
super(props);
this.state = {article: null};
}
componentDidMount() {
let self = this;
// I retrieve the list of articles ever saved, and filter them
// with the slug I passed as parameter / url
utils.loader(window.location.origin + '/all_articles.json', function (articles) {
self.setState({
article: articles.find(a => a.slug === self.props.match.params.slug)
});
});
}
render() {
// returns the correct data
console.log(this.props.match.params.slug);
const { article } = this.state;
return (
article !== null &&
<div>
<h1>{article.title}</h1>
<p>Tags: {article.tags}</p>
<p>{article.content}</p>
<img src={article.image.url} alt=""/>
</div>
)
}
}
Did I forgot about something?
Thank you in advance
Article component is not remounting when url params are changed, so componentDidMount method is not called. You should use componentWillReceiveProps method to check if params are changed.
componentDidMount() {
this.loadArticle(this.props.match.params.slug);
}
componentWillReceiveProps(newProps) {
if (this.props.match.params.slug !== newProps.match.params.slug) {
this.loadArticle(newProps.match.params.slug);
}
}
Working demo

React.js search bar always fetches the same content

I'm building a search engine with React.js, where I can look for GIPHY gifs using their API. Everytime I type a word(any word), it always loads the same gifs and when I erase and write another word, the gifs don't update.
index.js:
import React from 'react'; //react library
import ReactDOM from 'react-dom'; //react DOM - to manipulate elements
import './index.css';
import SearchBar from './components/Search';
import GifList from './components/SelectedList';
class Root extends React.Component { //Component that will serve as the parent for the rest of the application.
constructor() {
super();
this.state = {
gifs: []
}
this.handleTermChange = this.handleTermChange.bind(this)
}
handleTermChange(term) {
console.log(term);
let url = 'http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=aOfWv08Of7UqS6nBOzsO36NDvwYzO6io';
fetch(url).
then(response => response.json()).then((gifs) => {
console.log(gifs);
this.setState({
gifs: gifs
});
});
};
render() {
return (
<div>
<SearchBar onTermChange={this.handleTermChange} />
<GifList gifs={this.state.gifs} />
</div>
);
}
}
ReactDOM.render( <Root />, document.getElementById('root'));
search.js
import React, { PropTypes } from 'react'
import './Search.css'
class SearchBar extends React.Component {
onInputChange(term) {
this.props.onTermChange(term);
}
render() {
return (
<div className="search">
<input placeholder="Enter text to search for gifs!" onChange={event => this.onInputChange(event.target.value)} />
</div>
);
}
}
export default SearchBar;
Giflist:
import React from 'react';
import GifItem from './SelectedListItem';
const GifList = (props) => {
console.log(props.gifs);
const gifItems = props.gifs && props.gifs.data && props.gifs.data.map((image) => {
return <GifItem key={image.id} gif={image} />
});
return (
<div className="gif-list">{gifItems}</div>
);
};
export default GifList;
GifItem:
import React from 'react';
const GifItem = (image) => {
return (
<div className="gif-item">
<img src={image.gif.images.downsized.url} />
</div>
)
};
export default GifItem;
I can't seem to find where is the issue here. Is it because of this.handleTermChange = this.handleTermChange.bind(this) and there is no "update" state after?
Any help is welcome :) Thanks!
Its because, you are not putting the term value entered by user in the url, all the time you hit the api with static value term, here:
'http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=aOfWv08Of7UqS6nBOzsO36NDvwYzO6io';
Replace ' by ' (tick), like this:
let url = `http://api.giphy.com/v1/gifs/search?q=${term.replace(/\s/g, '+')}&api_key=aOfWv08Of7UqS6nBOzsO36NDvwYzO6io`;
Check MDN Doc for more details about Template Literals.

How to prevent browser from fetching the same href repeatedly

I am a freshman in react ,I want to write a react component of getting the member info of a team by teamId.
React code
import React from 'react';
import PropTypes from 'prop-types';
import UserTable from './pm_user_table';
import {Form,Modal,Input,Button} from 'antd';
const FormItem = Form.Item;
class PMBody extends React.Component{
constructor(props){
super(props);
this.state={
curTeam:this.props.curTeam,
memberList:[]
}
}
componentWillMount(){
console.log('component mount');
}
componentWillReceiveProps(nextProps){
if(nextProps.curTeam !== this.state.curTeam){
this.setState({curTeam:nextProps.curTeam});
}
}
render(){
let {getFieldProps} = this.props.form;
const teamId = this.state.curTeam;
var myFetchOptions={method: 'GET'};
fetch("http://localhost:3001/teamMembers/" +this.state.curTeam,myFetchOptions)
.then(response=>response.json())
.then(json => {
this.setState({memberList:json});
}
).catch(function(){
console.log("error");
});
let memberList = this.state.memberList;
const body = memberList !='' ?
<UserTable dataSource={memberList} actions={this.props.actions} />
:
''
;
return (
<div>
{body}
</div>
)
}
PMBody.PropTypes = {
curTeam:PropTypes.string.isRequired,
actions: PropTypes.object.isRequired
}
export default PMBody =Form.create({})(PMBody);
By the network view in chrome devtool,It seems that the browser request the same url repeatedly.
So why it fetch the same url repeately?
You're misunderstanding the purpose of the render() method.
React calls render() to update your component anytime anything changes. It must be pure and should not interact with anything else.
You should move that to componentDidMount().

Categories

Resources