Components called by API won't render - reactjs

I'm building an app in React. I'm new to React so please bear with me. This question is very simple. I am creating three components that are being called by an API (I'm using axios to call the API), but they simply won't render. I am not sure what I'm doing wrong.
import React, { Component } from "react";
import axios from "axios";
class Temperature extends Component {
showTemperature = () => {
let apiKey = "4cc79448ae57aa2b8557ec4dcd604fac";
let city = "Lisbon,pt";
let url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${apiKey}&units=metric`;
axios.get(url).then(function(response) {
let temp = Math.round(response.data.main.temp);
return temp;
});
};
render() {
return <div>{this.showTemperature()}</div>;
}
}
export default Temperature;
and then I call the component on App.js
<div>
<Temperature />
</div>
inside the App function. Nothing is rendering when I call the component. Any tips or what am I doing wrong? Thanks.
PS: I am importing the components into App:
import Temperature from "./Temperature";
EDIT: I want to add that I have this same app done in vanilla JavaScript and the API is working fine.

Try utilizing state to store your temperature value, like so:
import React, { Component } from "react";
import axios from "axios";
class Temperature extends Component {
constructor(props) {
super(props);
this.state = {
temp: 'Error' // Add default value to help determine if error is api call.
};
this.showTemperature = this.showTemperature.bind(this); // Bind 'this' context
}
showTemperature() { // This is how to properly declare a method in react.
let apiKey = "4cc79448ae57aa2b8557ec4dcd604fac";
let city = "Lisbon,pt";
let url = `https://api.openweathermap.org/data/2.5/weather?
q=${city}&appid=${apiKey}&units=metric`;
axios.get(url).then(function(response) {
let temp = Math.round(response.data.main.temp);
if (temp) { // Don't setstate if temp is undefined.
this.setState({temp: temp}); // Set your state
}
});
};
componentDidMount() {
this.showTemperature(); // Call your method on mount.
}
render() {
return <div>{this.state.temp}</div>;
}
}
export default Temperature;
I added comments to explain further errors in your code. You should be able to troubleshoot much better from here.

You have to import Temperature in App.js like this
import Temperature from 'path'

Your showTemperature method returns undefined. That's why It's not rendering anything.

Related

Calling ApolloClient GraphQl request inside componentDidMount method

I am using ApolloClient GraphQl query inside react class to fetch data from server:
import React, {Component} from 'react';
import {useCompanyLogo} from '../../queries/companyLogo';
class Logo extends Component {
constructor() {
super();
this.state = {logo: ""};
}
componentDidMount() {
const {error, loading, data} = useCompanyLogo();
if(loading) return <div>spinner</div>
if(error) return <div>error!</div>
const imageSource = data.companyLogo[0].image.urls[0];
this.setState({logo: imageSource});
}
render() {
return (
<div className="logo-area">
<img src={"http://computer-313:5000" + this.state.logo} alt="Businex-Logo" style={{width:"80px"}} />
</div>
);
}
}
export default Logo;
And the query is as below:
import {useQuery, gql} from "#apollo/client";
var COMPANY_LOGO = gql`
query CompanyLogo {
companyLogo {
image {
urls(first: 1)
}
}
}
`;
export const useCompanyLogo = () => {
const {error, data, loading} = useQuery(COMPANY_LOGO);
console.log(error, data, loading);
return {
error,
data,
loading
}
}
Everything works good when I use function instead of class But when I run this code I get the following error:
Uncaught Error: Invalid hook call. Hooks can only be called inside of the body of a function component.
According to the React.js documentation you cannot use Hooks inside of Class Components.
You can’t use Hooks inside a class component, but you can definitely mix classes and function components with Hooks in a single tree. Whether a component is a class or a function that uses Hooks is an implementation detail of that component. In the longer term, we expect Hooks to be the primary way people write React components.
You can try to use high order components and be able to pass the hooks into your Class Component that way.

next.js - getInitialProps does not work in component

Here's my code
import React from 'react'
import fetch from 'isomorphic-unfetch'
import Book from './Book'
function getNum(val) {
val = +val || 0;
return val;
}
class BookList extends React.Component {
static async getInitialProps(ctx) {;
const res = await fetch('/api/books');
const json = await res.json();
return { books: json }
}
render() {
var books = this.props.books;
For some reason "books" in the render function is undefined. Why doesn't getInitialProps work in a component?
getInitialProps can only be added to the default component exported by a page, adding it to any other component won't work.
getInitialProps works only at pages level, not at components level.
sgetInitialProps can not be used in children components, only in the default export of every page
https://nextjs.org/docs/api-reference/data-fetching/getInitialProps#caveats

reactjs data mapping, get data from server

I'm new reactjs
I'm trying to save data that I got from server like object(array) but I can't.
at render() function, what should I do to save data, I don't wanna display, just save to users (array) or something? I think that I should use "map" but I don't know how to do.
Next, I wanna save users to model.data like this. help me.
Since you just started using react, try using React Hooks instead of class style components. It's the recommended way.
If you just want to store the data without displaying anything you need somekind of a encapsulated/shared state. For example redux or context can help you with that. Since context is in-built and easier to use, here is an example:
First create a context
users-context.js
import React from "react";
export const UsersContext= React.createContext();
Now create a custom hook to store your state.
useUsers.js
import React, {useState, useEffect} from "react";
export const useUsers = () => {
const [users, setUsers] = useState([]);
const getUsers = () =>{
//your fetch
}
useEffect(()=>{ //equivalent to componentDidMount
getUsers();
}, [])
return {users, setUsers}
}
Then provide the context so every component in your app has access to that context.
App.jsx
import {UsersContext} from "./UsersContext";
const App = () => {
const contextValue = useUsers();
return (
<div className={'App'}>
<UsersContext.Provider value={contextValue}>
<Main/>
</UsersContext.Provider>
</div>
);
};
export default App;
If you want to use the state in a component, e.g. a profile page do this:
profile-page.jsx
import React, {useContext} from "react";
import {UsersContext} from "./UsersContext";
const ProfilePage = () => {
const {users} = useContext(UsersContext);
// now you can use it like
console.log(users)
return (...)
}
import { UsersContext } from './Components/usersData/users-context';
const getUsers = () => {
const {users} = UsersContext(UsersContext);
console.log(users);
return users;
}
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
data:[]
}
data.push(getUsers);`
}
}

Get redux state on window.scroll

I want to implement pagination. So when a user scrolls down to the bottom I want to make an api call. I see through window.scroll I can find position of scroll and can achieve that. However I want to access redux state to get certian data. Since this event is not bind by any component I won't be able to pass down data. What would be the correct approach in this scenario?
If I want to access redux store through a simple function How can I do that? And on scroll how do I make sure that only request goes through?
You can connect your component that does the scroll. or you can pass props to the component that have the store information. Those are the two recommended ways to reach your store. That being said you can also look at the context
class MyComponent extends React.Component {
someMethod() {
doSomethingWith(this.context.store);
}
render() {
...
}
}
MyComponent.contextTypes = {
store: React.PropTypes.object.isRequired
};
Note: Context is opt-in; you have to specify contextTypes on the component to get it.
Read up on React's Context doc It may not be a complete solution since it could be deprecated in a future version
Edit:
Per the comments with the clarity you provided you can just do this.
import React, { Component } from 'react';
import ReactDOM = from 'react-dom';
import _ from 'lodash';
const defaultOffset = 300;
var topOfElement = function(element) {
if (!element) {
return 0;
}
return element.offsetTop + topOfElement(element.offsetParent);
};
class InfiniteScroll extends Component {
constructor(props) {
super(props);
this.listener = _.throttle(this.scrollListener, 200).bind(this);
}
componentDidMount() {
this.attachScrollListener();
}
componentWillUnmount() {
this.detachScrollListener();
}
scrollListener () {
var el = ReactDOM.findDOMNode(this);
var offset = this.props.offset || defaultOffset;
var scrollTop = (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;
if (topOfElement(el) + el.offsetHeight - scrollTop - window.innerHeight < offset) {
this.props.somethingHere;
}
}
attachScrollListener() {
window.addEventListener('scroll', this.listener);
window.addEventListener('resize', this.listener);
this.listener();
}
detachScrollListener() {
window.removeEventListener('scroll', this.listener);
window.removeEventListener('resize', this.listener);
}
render() {
return (...)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(InfiniteScroll);
I added lodash to the import here so you can throttle the scroll listener function. you only want to call the handler function so many times a second or it can start lagging the page (depending on how heavy the listener function is)
The correct way to access your application state in components is the usage of react-redux and selectors functions.
react-redux provides a function which is called connect. You should use this function to define which values from our state you want to map to the props of the component so these will be available.
The function you need for this mapping is called mapStateToPropswhich returns an object with the values which should be passed to the component.
Also you can be define redux actions which should be made available in the component (e.g. for trigger the load of the next page). The function is called mapDispatchToProps.
Here an example:
import React from 'react';
import { connect } from 'react-redux';
import { getUsersPage } from './selectors';
import { loadUsersPage } from './actions';
class MyComponent extends React.Component {
handleScroll () {
this.props.loadUsersPage({ page: lastPage + 1 });
}
render () {
const users = this.props.users;
// ...
}
}
const mapStateToThis = (state) => {
return {
users: getUsers(state)
};
}
const mapDispatchToProps = (dispatch) => {
return {
loadUsersPage: (payload) => {
dispatch (loadUsersPage(payload));
}
}
};
export default connect()(MyComponent);

React: onChange is not a function

Am new to react and am trying to run unit test for a react app using jest,
The app using the Flux pattern and I have already wrote one component "WEATHER" and before move on to another one I wanted to write test for that component and take TDD approach.
My code running fine but the test fail with this error
TypeError: tree.props.onChange is not a function
The weather component code :
// #flow
import React from 'react';
import api from '../api';
import weatherStore from '../stores/weatherStore';
//read from weather store
let _getState = () => {
return {weather: weatherStore.returnWeather()};
};
export default class Weather extends React.Component {
constructor(proporties) {
super(proporties);
this._onChange = this._onChange.bind(this);
this.state = _getState();
}
componentWillUnmount() {
weatherStore.removeListener('change', this._onChange);
}
componentDidMount() {
api.getWeather();
weatherStore.on('change', this._onChange);
}
_onChange() {
this.setState(_getState());
}
render() {
let weatherState = this.state.weather.map(weather => {
return <div key={weather.id} className="pull-right row">
<div>
<span>{weather.main.temp} C</span><br />
</div>
</div>;
})
return <div>{weatherState}</div>;
}
}
Where the test code:
import React from 'react';
import Weather from '../js/components/Weather';
import renderer from 'react-test-renderer';
it('should render weather temp and city', ()=> {
const component = renderer.create(
<Weather />
);
let tree = component.toJSON();
expect(tree).toMatchSnapshot();
// manually trigger the callback
tree.props._onChange();
// re-rendering
tree = component.toJSON();
expect(tree).toMatchSnapshot();
});
Note since am using flow static type checker it always high light this line of code this._onChange = this._onChange.bind(this);
with error message saying : property '_onChange' property not found in the weather.
TypeError: tree.props.onChange is not a function
Have you tried updating your test with:
// manually trigger the callback
tree.props._onChange();
Note the dash.

Resources