Try to display data using Mobx from API - javascript

Im making React app that shows coins' data from API.
I did it with useEffect and it works fine, but now I'm trying to do the same using Mobx.
Im trying to create a store file that gets the data from an API and stores it, then passes it to App.js and then displays the data on screen.
Im new with Mobx. Please help me resolving my Issue
This is my useEffect:
useEffect(() => {
axios.get('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&order=market_cap_desc&per_page=100&page=1&sparkline=false')
.then(res => {
setCoins(res.data)
console.log(res.data)
}).catch(error => console.log(error))
}, []);
How can I convert this useEffect to Mobx in Store.js file?
For the first step I just want to display coins' name.
Thanks!

The structure should look like this one:
// Coins Store file
type Coin = {
name: string;
}
export class CointStore {
// not sure what is your coins data type, lets assume this is array
readonly coins = observable<Coin>([]);
constructor() {
makeAutoObservable(this);
}
getCoins() {
return axios.get('https://api.coingecko.com/api/v3/coins/markets')
.then(response => this.coins.replace(response.data);
}
}
...
// this is app.js file
import {observer} from 'mobx-react-lite'
import {createContext, useContext, useEffect} from "react"
import {CointStore} from './CointStore'
const CoinsContext = createContext<CointStore>()
const CoinsView = observer(() => {
const coinStore = useContext(CoinsContext);
useEffect(() => {
coinStore.getCoins()
}, []);
return (
<span>
{coinStore.coins.map(coin => <span>{coin.name}</span>)}
</span>
)
})
ReactDOM.render(
<CoinsContext.Provider value={new CointStore()}>
<CoinsView />
</CoinsContext.Provider>,
document.body
)

Related

Console log return an infinite value in react js

I want to display some datas from API using functional component instead of class component in react. In order to do so, I write useEffect and apparently work properly. The problem is, if I write console log, it would return an infinite value.
Any one can help me to solve this? I want the console log stop looping the value from my API. This is my source code. Thank you.
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
export default function FoodDetail() {
const { id } = useParams();
const [detail, setDetail] = useState([]);
useEffect(() => {
axios
.get("http://localhost:3004/foods/" + id)
.then((res) => {
setDetail(res.data);
console.log(detail)
})
.catch((error) => {
console.log(error);
});
});
return ()
}
if you want the get to only run once on component mount, you can use the code below:
import React, { useEffect, useState } from "react";
import { useParams } from "react-router-dom";
import axios from "axios";
export default function FoodDetail() {
const { id } = useParams();
const [detail, setDetail] = useState([]);
useEffect(() => {
axios
.get("http://localhost:3004/foods/" + id)
.then((res) => {
setDetail(res.data);
console.log(detail)
})
.catch((error) => {
console.log(error);
});
}, []);
return ()
}
The only difference is the inclusion of an empty dependency array as the second argument for useEffect(). Going forward, if you want to refetch this data based on the state of another variable changing, you can add that variable to the dependency array and this will refetch the data each time the value of that variable changes.
** edit **
To see the value of detail properly, you can remove the console.log from the first useEffect loop and add something like this to the code:
useEffect(()=> {
console.log(detail)
}, [detail])
your useeffect is running infinitely since you console log your call stack gets
full and you notice it so use following code so it will exactly run once like
componentdidmount
useEffect(() => {
axios
.get("http://localhost:3004/foods/" + id)
.then((res) => {
setDetail(res.data);
console.log(detail)
})
.catch((error) => {
console.log(error);
});
},[]); //add empty array as another parameter of useEffect so it run only
//once
Try adding an empty array to the UseEffect refreshing dependency.
UseEffect(() => {}, []) this empty array means UseEffect will only be triggered at component mounting, i guess yours would be called everytime component is re-rendered.
Example : UseEffect(() => {console.count('refresehd'), [detail]}) would be triggered everytime your detail changes
for more info check the UseEffect Documentation
Docs useEffect

How can I access data in another component?

I'm sorry if my question is too ridiculous, because I'm new to programming.
I created a project with React, I have 3 different components: navbar, sidebar and data.
I received json data from an api using hooks in my data component.
import { useEffect, useState } from "react";
export const Data = () => {
const [data, setData] = useState();
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => setData(json));
}, []);
};
Now, how can I access the "data" state in the data component from other components?
I don't want to use Context api, because I've heard that it is only used in cases where the state affects all components, like authentication
thanks in advance for your help
I have added few lines in your codesandbox. I think this is same that you want to achieve
codesandbox
Thank you to everyone who helped me. Actually, I realized that the title is not correct. What I wanted to do was to use the data brought outside the component inside the component. I did this by creating custom hooks. If anyone is curious, I leave the code below.
import { useEffect, useState } from "react";
export default function useData(id = 1) {
const [data, setData] = useState([]);
useEffect(() => {
fetch(`https://jsonplaceholder.typicode.com/todos/${id}`)
.then((response) => response.json())
.then((json) => setData(json));
}, []);
return { data };
}
Then I import and use the useData hook I created in any component.
(I'm not even sure it's called a hook.)
Example: const {data} = useData(4)
You could maybe use the module.exports function in the data component and then just call it in the necessary components. For example:
import { useEffect, useState } from "react";
export const Data = () => {
const [data, setData] = useState();
useEffect(() => {
fetch("https://jsonplaceholder.typicode.com/todos/1")
.then((response) => response.json())
.then((json) => setData(json));
}, []);
};
module.exports = data;
Then in the navbar component for example you call it and use it where it is necessary:
const Navbar = () => {
const data = require(../Data)
...
...
...
}
Hope it helps.

Is there a way to rearrange my state so that I can show the image URL?

I have a React component (also using TypeScript) that returns a photo to its parent component:
import React, { useEffect, useState } from "react";
import axios from "axios";
export const Photo = () => {
const [image, setImage] = useState<Image[]>([]);
// Backend API
const proxyUrl = "http://localhost:3001";
const api = "/photo";
interface Image {
src: string;
}
useEffect(() => {
(async function callAPI() {
axios
.get<Image>(proxyUrl + api)
.then((response) => {
setImage([
{
...response.data,
},
]);
})
.catch((error) => console.log(error));
})();
}, []);
if (image.length === 0) return <span>loading Image...</span>;
return <img src={String(image[0])} />;
The goal is to get an artist's profile picture from my backend. The only way that I can save the image in state is by using the spread operator, and when I do so, it SPREADS the URL - placing each taking each letter and placing it as a value in an object (see the screenshot below).
When I remove the spread operator, I get lots of errors. I need a way to retrieve the photo URL without breaking it up so that I can return and view it. Perhaps by changing the way I defined the interface? Or by setting up state differently?
Image of the URL being spread
first of all modify your useState like this :
const [image, setImage] = useState<String>([]);
and data is fetched you should setState like this :
setImage(response.data)
and delete Image from your get function type.
your return :
return <img src={image)} />;
With the help of the comments above by mortezashojaei, I was able to tweak the component and finally able to get it to work as intended. Adding the code here in case anyone's interested.
import React, { useEffect, useState } from "react";
import axios from "axios";
export const Photo = () => {
const [image, setImage] = useState<string>("");
// Backend API
const proxyUrl = "http://localhost:3001";
const api = "/photo";
useEffect(() => {
(async function callAPI() {
axios
.get(proxyUrl + api)
.then((response) => {
setImage(response.data);
console.log("RESPONSE.DATA");
console.log(response.data);
})
.catch((error) => console.log(error));
})();
}, []);
console.log("Image (state): ");
console.log(image);
if (!image) return <span>loading Image...</span>;
return <img src={image} />;
};

React Context API, Data loading twice. Once with the data and the other without

Here is how I have created a Context for simple program I am writing
import React, { useState, createContext, useEffect } from "react";
export const PhotoContext = createContext();
export const PhotoProvider = (props) => {
const [photo, setPhoto] = useState([]);
useEffect(() => {
console.log("Use Effect Runs HAHAHAH");
console.log("HAHAHAHAHAHAHAH");
fetchPhotos();
async function fetchPhotos() {
const url =
"https://raw.githubusercontent.com/bobziroll/scrimba-react-bootcamp-images/master/images.json";
fetch(url)
.then((res) => res.json())
.then((arr) => {
setPhoto(arr);
})
.catch(console.log("ERROR"));
}
}, []);
return (
<PhotoContext.Provider value={[photo, setPhoto]}>
{props.children}
</PhotoContext.Provider>
);
};
There is another file where I want to load the data in the photos variable. Here is the code for it. I have used setTimeout to see where exactly is the problem. It seems whenever the statement in setTimeout runs, the value in console in returned twice. First, it is empty and the second has the actual value. But since, I try to access the photos.url, and since the first time it is undefined, the program collapses.
import React, { useState, useContext } from "react";
import { PhotoContext } from "../Context/PhotoContext";
const Photos = (props) => {
const [photos, values] = useContext(PhotoContext);
setTimeout(() => {
console.log(photos[0].url);
}, 3000);
return <div>{}</div>;
};
export default Photos;
Help would be really appreciated.
Didn't see the problem. I created sandbox for your example.
https://codesandbox.io/s/inspiring-lovelace-5r1gb?file=/src/App.js

Cannot read states in Redux with React Hooks, Cannot read property '_id' of null

I have a MERN Web-app, which I am learning React Hooks.
What I am trying to do : Access the states in my Redux.
When i refresh the page,
The error : TypeError: Cannot read property '_id' of null
I am not able to access it when I clearly see the states in my redux developer tools.
I have tried console.log(auth.isAuthenicated) but it returns null. However, when I do console.log(auth), it returns [object,object]. Which confuses me because I can't get inside.
Currently, I am researching and will look into react-persist. I was wondering if anyone can help me with my issue without react persist or explain why it might be a good idea to use it.
My redux :
token(pin):"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjVlNDFmYTNhOWIwZjk0NmU5N2Q5MmY4MiIsImlhdCI6MTU4Mzk0NzA5MSwiZXhwIjoxNTgzOTUwNjkxfQ.pysX20n4cxKK5NqcXPosIejSvCN3pbcSNpQvEOX9kBE"
isAuthenticated(pin):true
isLoading(pin):false
_id(pin):"5e41fa3a9b0f946e97d92f82"
name(pin):"admin"
email(pin):"admin#gmail.com"
date(pin):"2020-02-11T00:50:02.183Z"
__v(pin):0
snippets of my code :
import React, { useState, useEffect } from "react";
import { TiDelete } from "react-icons/ti";
import Restaurants from "../Restaurant/Restaurants";
import NutritionalGraphs from "../D3Graphs/NutritionalGraphs";
import { connect, useDispatch, useSelector } from "react-redux";
import axios from "axios";
import { addItem, deleteItem } from "../../../actions/itemActions";
import IngredientsPredictions from "../Predictions/IngredientsPredictions";
import { loadUser } from "../../../actions/authActions";
import { createSelector } from "reselect";
const UserProfile = props => {
const dispatch = useDispatch();
const [newUserFavorite, setNewUserFavorite] = useState("");
const [favArray, setFavArray] = useState([]);
const tokenRecognized = useSelector(state => state.auth.token);
// const userID = useSelector(state => state.auth.user._id);
const auth = useSelector(state => state.auth);
const userStates = createSelector();
// name
// name => props.auth.user.name,
// userID => props.auth.user._id
// foodFavoritesArray => foodFavoritesArray.state.item.items
useEffect(() => {
dispatch(loadUser(tokenRecognized));
// console.log(userStates.userID);
console.log(auth.isAuthenicated);
axios
// .get(`/api/items/item/${userStates.userID}`)
.get(`/api/items/item/${auth.user._id}`)
.then(res => {
return res.data;
})
.then(json => {
setFavArray(json);
})
.catch(err => console.log(err));
}, [userStates.userID]);
console.log(favArray);
it is breaking at : .get(`/api/items/item/${auth.user._id}`):
Big thank you for the read.
You need to wait for your loadUser action to complete before you can access the data. I assume that it makes an async request. You need to that in two steps:
useEffect(() => {
// fetch user data when component mounts
dispatch(loadUser(tokenRecognized));
}, []);
useEffect(() => {
// check if user has been fetched (will not be the case on mount)
if (auth.user) {
axios
.get(`/api/items/item/${auth.user._id}`)
.then(res => {
return res.data;
})
.then(json => {
setFavArray(json);
})
.catch(err => console.log(err));
}
}, [auth.user]); // perform this when `auth.user` changes

Categories

Resources