I am trying to fetch data from the backend and then render a custom component with props as the data fetched from the backend. Here's a snippet:
import { useEffect } from 'react';
import axios from 'axios';
import Card from './Card';
export default function DesignList() {
useEffect(() => {
async function populate() {
const response = await axios({
method: 'POST',
baseURL: 'http://localhost:3001',
url: '/recommend',
headers: { 'Content-Type': 'application/json' },
});
response.data.forEach(recommendation => {
document.getElementById('design-list').append(<Card
title={recommendation.title}
});
}
populate();
})
return (
<div id='design-list' />
)
}
I also tried to use React.createElement(<Card .../>), pushed it in an array and tried to render it but I got the same output as described below.
This gives an output on the screen but not as a HTML component. The output for each component is [object Object]
output
How do I render the components dynamically after fetching the props data from backend?
Use state for showing recommendations, not dom append.
import { useEffect, useState } from "react";
import axios from "axios";
import Card from "./Card";
export default function DesignList() {
const [recommendations, setRecommendations] = useState([]);
useEffect(() => {
async function populate() {
const response = await axios({
method: "POST",
baseURL: "http://localhost:3001",
url: "/recommend",
headers: { "Content-Type": "application/json" },
});
setRecommendations(response.data);
}
populate();
});
return (
<div id="design-list">
{recommendations.map((item) => (
<Card title={item.title} />
))}
</div>
);
}
Related
I would like to have following, but working code in my http-axios file:
import axios from "axios";
import {useAuthContext} from './services/useAuthContext'
const {user} = useAuthContext();
export default axios.create({
baseURL: "http://localhost:4000/api/",
headers: {
"Content-type":"application/json",
"Authorization": `Bearer ${user.token}`
}
});
I can not understand how to make my code working because of the continuous dependencies i have.
I call this axios.create export later in an ./services/imgs.js file:
import http from "../http-axios";
class VMsDataService {
getLatest(page=0){
return http.get(`vms/latest`);
}
}
which i use in App.js file:
import VMsDataService from "./services/vms";
VMsDataService.getLatest()
.then(response => {
//
}).catch(e => {
//
});
So my question is. How can i restructure my code to be able to retrieve dynamic data within my axios config and also have this class based functionality i'm having right now. Main point of this is to have dynamic Authorization header taken from useAuthContext component
I tried to turn everything into components but it wasn't successful.
A way to create a reusable component, is to create a custom hook.
For example a custom hook that will return user object and set Authorization :
import axios from "axios";
import {useAuthContext} from './services/useAuthContext'
const useAxios = () => {
const {user} = useAuthContext();
return axios.create({
baseURL: "http://localhost:4000/api/",
headers: {
"Content-type": "application/json",
"Authorization": `Bearer ${user.token}`,
},
});
};
export default useAxios;
Then you can use this custom hook in VMsDataService.
I am not used to class component, so I wrote it in functional component. I think it is a lot easier :
import React from "react";
import useAxios from "../hooks/useAxios";
const VMsDataService = () => {
const http = useAxios();
const getLatest = (page = 0) => {
return http.get(`vms/latest`);
};
return {getLatest};
};
export default VMsDataService;
Hope I understand clearly what you were asking for !
I am trying to learn React by making a motor cycle spec search web application.
I am making two axios requests in /api/index.js, and I am getting an error saying
'429 (Too Many Requests)'.
What am I doing wrong here?
/api/index.js
import axios from "axios";
const options1 = {
method: 'GET',
url: 'https://motorcycle-specs-database.p.rapidapi.com/model/make-name/Yamaha',
headers: {
'X-RapidAPI-Host': 'motorcycle-specs-database.p.rapidapi.com',
'X-RapidAPI-Key': 'MyAPIKey'
}
};
const options2 = {
method: 'GET',
url: 'https://motorcycle-specs-database.p.rapidapi.com/make',
headers: {
'X-RapidAPI-Host': 'motorcycle-specs-database.p.rapidapi.com',
'X-RapidAPI-Key': 'MyAPIKey'
}
};
export const makeList = async()=>{
try{
const {data} = await axios.request(options2);
console.log('list of all makes is like this now', data);
return data;
}
catch(error){
}
}
export const fetchData = async ()=>{
try{
const {data} = await axios.request(options1);
return data;
}
catch(error){
}
}
and this is where I'm trying to use the data.
App.js
import logo from './logo.svg';
import './App.css';
import {fetchData, makeList} from './api/index';
import React, {Component} from 'react';
class App extends React.Component{
state = {
data:[],
makes:[],
}
async componentDidMount(){
const fetchedData = await fetchData();
const fetchedMakeList = await makeList();
this.setState({data:fetchedData, makes:fetchedMakeList});
//this.setState({makes:fetchedMakeList});
console.log('list of all makes in componentDIDMOUNT is like ', fetchedMakeList);
//why is this undefined??
}
render(){
return (
<div className="App">
<header className="App-header">
<h1>Some line-ups from YAMAHA</h1>
{partOfTheArray.map(data=>{
return <p>{data.name}</p>
})}
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Open React
</a>
</header>
</div>
);
}
}
export default App;
I am only requesting 2 requests, but I am getting this error message.
Assuming that you're trying to fetch data when component mounts, here is a better approach to do so:
// Import useState and useEffect
import React, {useState, useEffect} from 'react';
export default function SomeComponent() {
let [data, setData] = useState(null)
// use an useEffect with empty dependecy(empty [] as a dependecy) to fetch the data
// empty [] makes sure that you're fetching data only once when the component mounts
useEffect(() => {
fetchData().then(res => {
// check status for response and set data accordingly
setData(res.data)
// log the data
console.log(res.data)
})
},[])
return (
<div className="App">
</div>
);
}
You need to update your fetchData() function as well.
export const fetchData = async ()=>{
try{
const response = await axios.request(options1);
// return the whole response object instead of only the data.
// this helps in error handling in the component
return response;
}
catch(error){}
}
I hope it helps!
I have a rails + react app (separate projects) and I've made an ActionCable websocket for sending simple messages from backend to the frontend. The websocket works, i can see everything on the frontend but I can't see the updates in real-time, only after refresh. I don't know how to implement the real time update.
Here is my code:
import PropTypes from 'prop-types'
import { Switch, Route } from 'react-router-dom'
import actionCable from 'actioncable'
import { DUMMY_QUERY } from 'Queries'
// app/javascript/packs/messages.js
import React, { useState, useEffect } from 'react'
import ReactDOM from 'react-dom'
import MessagesChannel from './channels/messages_channel'
function useForceUpdate(){
const [value, setValue] = useState(0); // integer state
return () => setValue(value => value + 1); // update the state to force render
}
function Dummy() {
const [messages, setMessages] = useState([])
const [message, setMessage] = useState('')
const [rerender, setRerender] = useState(false);
useEffect(() => {
MessagesChannel.received = (data) => {
setMessages(data.messages)
console.log(data)
}
}, [])
/*const handleSubmit = async (e) => {
e.preventDefault()
// Add the X-CSRF-TOKEN token so rails accepts the request
await fetch('http://localhost:3000/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ message }),
})
setMessage('')
}*/
return (
<div>
<ul>
{messages.map((message) => (
<li key={message.id}>{message.content}</li>
))}
</ul>
</div>
)
}
export default Dummy
Active cable also hat a frontend component. I have never used it together with react, but with pure js it looks something like that:
Import the lib (consumer.js):
import { createConsumer } from "#rails/actioncable"
export default createConsumer()
Load all channels defined in your repo (index.js):
const channels = require.context('.', true, /_channel\.js$/)
channels.keys().forEach(channels)
Define a js file for each channel (message_channel.js):
import consumer from "./consumer"
consumer.subscriptions.create("MessagesChannel", {
connected() {
// Called when the subscription is ready for use on the server
},
disconnected() {
// Called when the subscription has been terminated by the server
},
received(data) {
// Called when there's incoming data on the websocket for this channel
console.log(data)
}
});
Edit: Just found out there is also a npm package for the frontend components.
I just started learning javascript and react-redux, and I'm using useEffect to call POST method. So, I am wondering how to make it not send request to my backend whenever I open or refresh website
my HTTP Post looks like:
export const sendItemData = (items) => {
return async () => {
const sendRequest = async () => {
const response = await fetch("http://localhost:51044/api/Items", {
method: "POST",
body: JSON.stringify(items.Items),
headers: {
"Content-Type": "application/json",
Accept: "application/json",
},
credentials: "same-origin",
});
if (!response.ok) {
throw new Error("Sending data failed!");
}
};
try {
await sendRequest();
} catch (error) {
console.log(error);
}
};
};
and my App.js looks like:
import React from "react";
import { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import Items from "./components/Items ";
import { sendItemData } from "./store/items-actions";
function App() {
const dispatch = useDispatch();
const sendItems = useSelector((state) => state.items);
useEffect(() => {
dispatch(sendItemData(sendItems));
}, [sendItems, dispatch]);
return <Items />;
}
export default App;
Ok, this is my way, tray it
export function MyComp(props) {
const initial = useRef(true)
useEffect(() => {
if (!initial.current) {
// Yoyr code
} else {
// Mark for next times
initial.current = false;
}
}, [args]);
return (
<YourContent />
);
}
I am testing api request call using jest and react testing library , here is my codes.
Live demo live demo
utils/api.js
import axios from "axios";
const instance = axios.create({
withCredentials: true,
baseURL: process.env.REACT_APP_API_URL,
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
});
export default instance;
export async function get(url, params) {
const response = await instance({
method: "GET",
url: url,
params: params,
});
return response;
}
mock/api.js
const mockResponse = {
data: {
attributes: {
first_name: "Jeff",
last_name: "Bezo's",
},
},
};
export default {
get: jest.fn().mockResolvedValue(mockResponse),
};
Home.js
import React, { useState, useEffect } from "react";
function Home() {
const [user, setUser] = useState();
const getUser = useCallback(async () => {
try {
const response = await api.get("/users/self");
console.log("data response..", response.data);
setUser(response.data.attributes.first_name);
} catch (error) {}
}, [dispatch]);
useEffect(() => {
getUser();
}, []);
return <div data-testid="user-info" > Welcome {user}</div>;
}
export default Home;
Home.test.js
import React, { Suspense } from "react";
import { render, cleanup, screen } from "#testing-library/react";
import "#testing-library/jest-dom/extend-expect";
import Home from "../../editor/Home";
import { Provider } from "react-redux";
import { store } from "../../app/store";
import Loader from "../../components/Loader/Loader";
const MockHomeComponent = () => {
return (
<Provider store={store}>
<Suspense fallback={<Loader />}>
<Home />
</Suspense>
</Provider>
);
};
describe("Home component", () => {
it("should return a user name", async () => {
render(<MockHomeComponent />);
const userDivElement = await screen.findByTestId(`user-info`);
expect(userDivElement).toBeInTheDocument();
screen.debug();
});
afterAll(cleanup);
});
Problem:
When I run npm run test test is passed but in the screen.debug() results I dont see the user name returned as expected I just see Welcome. but it should be welcome Jeff.
What am I doing wrong here? any suggestions will be appreciated