I have outsourced a fetch function into lib/cityData.js
const fetch = require('cross-fetch');
const dev = process.env.NODE_ENV !== 'production';
const server = dev ? 'http://localhost:3000' : 'https://your_deployment.server.com';
const fetchCityData = (city) => {
const options = {
method: `POST`,
};
fetch(`${server}/api/weather?city=${city}`, options)
.then((response) => {
if(response.ok){
return response.json().then(data => console.log(data))
}
throw new Error('Api is not available')
})
.catch(error => {
console.error('Error fetching data in city data: ', error)
})
}
//fetchCityData('London')
module.exports.fetchCityData = fetchCityData;
Data is an object, so fetchCityData('London') returns
{
location: {
name: 'London',
region: 'City of London, Greater London',
country: 'United Kingdom',
lat: 51.52,
lon: -0.11,
tz_id: 'Europe/London',
localtime_epoch: 1632394483,
localtime: '2021-09-23 11:54'
},
current: {
last_updated_epoch: 1632393900,
last_updated: '2021-09-23 11:45',
temp_c: 18,
temp_f: 64.4,
is_day: 1,
condition: {
text: 'Partly cloudy',
icon: '//cdn.weatherapi.com/weather/64x64/day/116.png',
code: 1003
},
wind_mph: 13.6,
wind_kph: 22,
wind_degree: 250,
wind_dir: 'WSW',
pressure_mb: 1020,
pressure_in: 30.12,
precip_mm: 0,
precip_in: 0,
humidity: 73,
cloud: 75,
feelslike_c: 18,
feelslike_f: 64.4,
vis_km: 10,
vis_miles: 6,
uv: 4,
gust_mph: 11.2,
gust_kph: 18
}
}
So, now I have a component and this needs this data. The process is as follows
In input user types city
Auto select gets fired
onSelect city is set (setCity(city))
This happens in the component SearchBar.js. Then city is being passed to the component ForecastButtons.js
This component takes the city and then onClick it should call my function above fetchCityData.js and return current temperature for the selected city. Before my fetchCityData function was part of ForecastButtons component, but I needed to outsource it, so now, of course the code is broken:
import React, { useState } from 'react';
import fetchCityData from '../lib/cityData'
export const ForecastButtons = ({ city }) => {
const [payload, setPayload] = useState(null)
const [error, setError] = useState(null)
const [loading, setLoading] = useState(true)
const location = payload?.location?.name;
const currentTemp = payload?.current?.temp_c;
return(
<div className="sm:col-span-2">
<p className="block text-sm font-medium text-gray-700">Select forecast</p>
<button onClick={fetchCityData} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded" type='button'>
Today
</button>
<p key={city?.location?.id} className='my-5'>
{ location ? `Current weather in ${location} is ${currentTemp} degrees ` : 'Please search for city to see current weather'}
</p>
</div>
)
}
setPayload in this component was called after fetch returned json. Now the payload is basically my data. Should I import the function somehow and setPayload(data)? I am new to react, this is way to complex for me. How do I use fetchCityData, setPayload and onClick in the button of my component still get the weather?
So I did just quick sketch how it could be. As I sad I transformed fetchCityData to hook usefetchCityData which return [data, loading, error]. We call that hook providing city. Inside hook when city changing useEffect calls server, updates all states, and returns [data, loading, error]. I am using my fake data and timeout to imitate network connection, also you can get fake error from server.
hooks.js:
// import axios from "axios";
import React from "react";
const useFetchCityData = (city) => {
const [data, setData] = React.useState();
const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState(false);
React.useEffect(() => {
const dev = process.env.NODE_ENV !== "production";
const server = dev
? "http://localhost:3000"
: "https://your_deployment.server.com";
setData(undefined);
setLoading(true);
setError(false);
const options = {
method: `POST`,
};
// axios(`${server}/api/weather?city=${city}`, options)
new Promise((resolve, reject) => {
setTimeout(() => {
if (Math.random() > 0.2) {
resolve(`data from server for ${city}`);
}
reject();
}, 2000);
})
.then((data) => {
setData(data);
setLoading(false);
})
.catch((error) => {
console.error("Error fetching data in city data: ", error);
setLoading(false);
setError(true);
});
}, [city]);
return [data, loading, error];
};
export { useFetchCityData };
App.js:
import React from "react";
import { useFetchCityData } from "./hooks";
const App = () => {
const [city, setCity] = React.useState("London");
const [data, loading, error] = useFetchCityData(city);
return (
<div>
<div>data: {data}</div>
<div>loading: {loading.toString()}</div>
<div>error: {error.toString()}</div>
</div>
);
};
export default App;
Another solution would simply be:
cityData.js
const fetch = require('cross-fetch');
const dev = process.env.NODE_ENV !== 'production';
const server = dev ? 'http://localhost:3000' : 'https://your_deployment.server.com';
const fetchCityData = (city) => {
const options = {
method: `POST`,
};
return fetch(`${server}/api/weather?city=${city}`, options)
.then((response) => {
if(response.ok){
return response.json()
}
throw new Error('Api is not available')
})
.catch(error => {
console.error('Error fetching data in city data: ', error)
})
}
ForecasButtons.js
import React, { useState, useEffect } from 'react';
import { fetchCityData } from '../lib/cityData'
export const ForecastButtons = ({ city }) => {
const [payload, setPayload] = useState(null)
const getData = () => {
fetchCityData(city).then((payload) => setPayload(payload));
}
const location = payload?.location?.name;
const currentTemp = payload?.current?.temp_c;
return(
<div className="sm:col-span-2">
<p className="block text-sm font-medium text-gray-700">Select forecast</p>
<button onClick={getData} className="mt-1 bg-transparent hover:bg-blue-500 text-blue-700 font-semibold hover:text-white py-2 px-4 border border-blue-500 hover:border-transparent rounded" type='button'>
Today
</button>
<p key={city?.location?.id} className='my-5'>
{ location ? `Current weather in ${location} is ${currentTemp} degrees ` : 'Please search for city to see current weather'}
</p>
</div>
)
}
It works, but as a beginner, I can't tell if this solution has any downsides.
Related
I'm new to React, and I'm trying to make a recpie app with react, right know I want to save the data in json file from the add form. so I can save the data but when I want to redirect the user to the home page using useEffict with navigate. I can't go to the create page when adding navigate to the useEffict.
Create file code:
import { useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom";
import { useFetch } from "../../hooks/useFetch";
// Styles
import "./Create.css";
export default function Create() {
const [title, setTitle] = useState("");
const [method, setMethod] = useState("");
const [cookingTime, setCookingTime] = useState("");
const [newIngredient, setNewIngredient] = useState("");
const [ingredients, setIngredients] = useState([]);
const { postData, data } = useFetch("http://localhost:3000/recipes", "POST");
const ingredientsInput = useRef(null);
const navigate = useNavigate();
// Methods
const handleSubmit = (e) => {
e.preventDefault();
postData({
title,
ingredients,
method,
cookingTime: cookingTime + " minutes",
});
};
const handleAdd = (e) => {
e.preventDefault();
const ing = newIngredient.trim();
if (ing && !ingredients.includes(ing)) {
setIngredients((preIng) => [...preIng, ing]);
}
setNewIngredient("");
ingredientsInput.current.focus();
};
useEffect(() => {
if (data) {
navigate("/");
console.log(data);
}
}, [data, navigate]);
return (
<div className="create">
<form onSubmit={handleSubmit}>
<label>
<span>Recipe Title:</span>
<input
type="text"
onChange={(e) => setTitle(e.target.value)}
value={title}
required
/>
</label>
<label>
<span>Recipe ingredients:</span>
<div className="ingredients">
<input
type="text"
onChange={(e) => setNewIngredient(e.target.value)}
value={newIngredient}
ref={ingredientsInput}
/>
<button onClick={handleAdd} className="btn">
Add
</button>
</div>
</label>
{ingredients.length > -1 && (
<p>
Current ingredients:{" "}
{ingredients.map((ing) => (
<span key={ing}>{ing}, </span>
))}
</p>
)}
<label>
<span>Recipe Method:</span>
<textarea
onChange={(e) => setMethod(e.target.value)}
value={method}
required
/>
</label>
<label>
<span>Recipe Time (minutes):</span>
<input
type="number"
onChange={(e) => setCookingTime(e.target.value)}
value={cookingTime}
required
/>
</label>
<button className="btn">Submit</button>
</form>
</div>
);
}
useFetch file code:
import { useState, useEffect } from "react";
export const useFetch = (url, method = "GET") => {
const [data, setData] = useState(null);
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState(null);
const [option, setOption] = useState(null);
const postData = (data) => {
setOption({
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(data),
});
};
useEffect(() => {
const controller = new AbortController();
const fetchData = async (fetchOption) => {
setIsPending(true);
try {
const res = await fetch(url, {
...fetchOption,
signal: controller.signal,
});
if (!res.ok) {
throw new Error(res.statusText);
}
const data = await res.json();
setIsPending(false);
setData(data);
setError(null);
} catch (err) {
if (err.name === "AbortError") {
console.log("the fetch was aborted");
} else {
setIsPending(false);
setError("Could not fetch the data");
}
}
};
if (method === "GET") {
fetchData();
}
if (method === "POST") {
fetchData(option);
}
return () => {
controller.abort();
};
}, [url, option, method]);
return { data, isPending, error, postData };
};
I don't know from where the issue came.
The problem was from useFetch file. when I want to do a post request I shoud cheack if the option useState has a value.
Before I was just check if there is a post method:
const [option, setOptions] = useState(null);
if (method === "POST") {
fetchData(option);
}
Know I'm checking if there is a value in option
const [option, setOptions] = useState(null);
if (method === "POST" && option) {
fetchData(option);
}
You basically trying to add a variable that is not a react state variable into the useEffect on update
const [recipes, setReceipies] = useState();
useEffect(async ()=> { const {data} = awawit useFetch("http://localhost:3000/recipes", "POST")
setReceipies(data);
},[])
navigate("/");
},[recipes]);
Or ofc you can navigate all the way from the mounting useEffect
Good Luck
after you save the data, simply add this code
const history = createBrowserHistory()
history.push(`/`)
I have big apps, that use history, and I never had a problem with it.
and I recomend you to use SWR for data-fetching - React Hooks for Data Fetching.
very simple and powerfull tool:
https://swr.vercel.app/
am trying update my todo list in my project using useContext api, i want to remove completed if i click of input checkbox but whenever i click on it am getting this error TypeError: Failed to execute 'fetch' on 'Window': Invalid name at _callee$, i have check the file name to see if am missing out something but is correct. please can someone help me out to know what am doing wrong here
import { createContext, useState } from "react"
const TodosContext = createContext()
const TodosProvider = ({children}) => {
const [todos, setTodos] = useState([])
const updatedTodos = async (updateTodo) => {
try{
const res = await fetch("/api/updateTodo", {
method: "PUT",
body: JSON.stringify(updateTodo),
headers: {"content-[type]": "application/json"}
})
await res.json()
setTodos((prevTodo) => {
const existingTodos = [...prevTodo]
const existingTodo = existingTodos.find((todo) => todo.id === updateTodo.id)
existingTodo.fields === updateTodo.fields
return existingTodos
})
} catch(err){
console.error(err)
}
}
return (
<TodosContext.Provider value={{
addTodos,
todos,
setTodos,
updatedTodos
}}
>
{children}
</TodosContext.Provider>
)
}
export {TodosContext, TodosProvider}
import {TodosContext} from '../context/todoContext'
import { useContext } from "react"
export default function Todo ({todo}){
const {updatedTodos} = useContext(TodosContext)
const handleToggle = () => {
const updateFields = {
...todo.fields,
completed: !todo.fields.completed
}
const updateTodo = {id: todo.id, fields: updateFields}
updatedTodos(updateTodo)
}
return (
<li className="flex items-center py-4 px-4 my-2 bg-white shadow-xl">
<input type="checkbox" name="completed" id="completed" className="form-checkbox cursor-pointer mr-2 h-5 w-5" checked={todo.fields.completed}
onChange={handleToggle}
/>
<p className={`flex-1 text-gray-800 font-semibold ${todo.fields.completed? "line-through" : ""}`}>{todo.fields.description}</p>
</li>
)
}
I think your header is invalid headers: {"content-[type]": "application/json"} should be {"Content-Type": "application/json"}, this is causing the error
I'm trying to make a sport/tinder like app for a school project from a friend of mine. It came together well on my localhost, but for him it was a requirement to host it online. Not really a professional in hosting, but I was a bit familiar with Heroku. I used a client and a server side for my application, so I build the client side and put it into the server side folder. This server side is hosted on the Heroku page. But whenever I try to login, it won't work and I get this error message in my console.
TypeError: Cannot read properties of undefined (reading 'map')
The error says it is caused by this line of code.
const matchedUserIds = matches.map(({user_id}) => user_id)
This is the whole MatchDisplay file that is used in my Dashboard. I'm using a MongoDB for the storage of my users.
import axios from "axios";
import { useEffect, useState } from "react";
import { useCookies } from "react-cookie";
const MatchesDisplay = ({ matches, setClickedUser }) => {
const [matchedProfiles, setMatchedProfiles] = useState(null);
const [cookies, setCookie, removeCookie] = useCookies(null);
const [matched, setMatched] = useState(null);
const matchedUserIds = matches.map(({ user_id }) => user_id);
const userId = cookies.UserId;
const getMatches = async () => {
try {
const response = await axios.get(
"https://[app].herokuapp.com/users",
{
params: { userIds: JSON.stringify(matched()) },
}
);
setMatchedProfiles(response.data);
} catch (error) {
console.log(error);
}
};
useEffect(() => {
getMatches();
}, [matches]);
const filteredMatchedProfiles = matchedProfiles?.filter(
(matchedProfile) =>
matchedProfile.matches.filter(
(profile) => profile.user_id === userId
).length > 0
);
return (
<div className="matches-display">
{filteredMatchedProfiles?.map((match) => (
<div
key={match.user_id}
className="match-card"
onClick={() => setClickedUser(match)}
>
<div className="img-container">
<img
src={match?.url}
alt={match?.first_name + "profile"}
/>
</div>
<h3>{match?.first_name}</h3>
</div>
))}
</div>
);
};
export default MatchesDisplay;
Any help is welcome. If you need more code examples, please reply ;)
EDIT
The ChatContainer that passes the user to the MatchesDisplay.
import ChatHeader from "./ChatHeader";
import MatchesDisplay from "./MatchesDisplay";
import ChatDisplay from "./ChatDisplay";
import { useState } from 'react';
const ChatContainer = ({user}) => {
const [ clickedUser, setClickedUser] = useState(null)
return (
<div className="chat-container">
<ChatHeader user={user}/>
<div>
<button className="option" onClick={() => setClickedUser(null)}>Matches</button>
<button className="option" disabled={!clickedUser}>Chat</button>
<button className="option" >Prices</button>
</div>
{!clickedUser && <MatchesDisplay matches={user.matches} setClickedUser={setClickedUser}/>}
{clickedUser && <ChatDisplay user={user} clickedUser={clickedUser}/>}
</div>
)
}
export default ChatContainer
The Dashboard that passes the user to the Chatcontainer.
import TinderCard from 'react-tinder-card';
import {useEffect, useState} from 'react';
import {useCookies} from 'react-cookie';
import ChatContainer from '../components/ChatContainer'
import axios from "axios";
const Dashboard = () => {
const [user, setUser] = useState(null)
const [genderedUsers, setGenderedUsers] = useState(null)
const [lastDirection, setLastDirection] = useState(null)
const [cookies, setCookie, removeCookie] = useCookies(['user'])
const [matchedUserIds, setMatchedUserIds] = useState(null)
const [filteredGenderedUsers, setFilteredGenderedUsers] = useState(null)
const userId = cookies.UserId
const getUser = async () => {
try {
const response = await axios.get('https://funfit-webpage.herokuapp.com/user', {
params: {userId}
})
return setUser(response.data)
} catch (error) {
console.log(error)
}
}
const getGenderedUsers = async () => {
try {
const response = await axios.get('https://funfit-webpage.herokuapp.com/gendered-users', {
params: {gender: user?.gender_interest}
})
return setGenderedUsers(response.data)
} catch (error) {
console.log(error)
}
}
useEffect(() => {
getUser()
}, [])
useEffect(() => {
setMatchedUserIds(user?.matches.map(({user_id}) => user_id).concat(userId))
if (user) return getGenderedUsers()
}, [user])
useEffect(() => {
if (genderedUsers) {
return setFilteredGenderedUsers(genderedUsers?.filter(
genderedUser => !matchedUserIds.includes(genderedUser.user_id)
))
}
}, [genderedUsers])
const updateMatches = async (matchedUserId) => {
try {
await axios.put('https://funfit-webpage.herokuapp.com/addmatch', {
userId,
matchedUserId
})
return getUser()
} catch (error) {
console.log(error)
}
}
const swiped = (direction, swipedUserId) => {
console.log(direction, swipedUserId)
if (direction === 'right') {
updateMatches(swipedUserId)
}
return setLastDirection(direction)
}
const outOfFrame = (name) => {
console.log(name + ' left the screen!')
}
return (<>
{user && <div className="dashboard">
<ChatContainer user={user}/>
<div className="swipe-container">
<div className="card-container">
{filteredGenderedUsers?.map((genderedUser) =>
<TinderCard
className='swipe'
key={genderedUser.user_id}
onSwipe={(dir) => swiped(dir, genderedUser.user_id)}
onCardLeftScreen={() => outOfFrame(genderedUser.first_name)}>
<div style={{backgroundImage: 'url(' + genderedUser.url + ')'}} className='card'>
<h3>{'Name: ' + genderedUser.first_name} <br/> {'Sport: ' + genderedUser.about}</h3>
</div>
</TinderCard>)}
<div className="swipe-info">
{lastDirection ? <p>You swiped {lastDirection}</p> : <p/>}
</div>
</div>
</div>
</div>}
</>)
}
export default Dashboard
I am using vercel to deploy but I cannot figure out how to set up environmental variables, so I want to try method using fetch("/data.json"). I also have custom hook for fetching data.
But this does not work and I don't even see data on my local.
data.json file is directly inside /public folder. Can someone help me?
useFetch.js
import { useState, useEffect } from "react";
export const useFetch = (url, method = "GET") => {
const [data, setData] = useState(null);
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState(null);
const [options, setOptions] = useState(null);
const postData = (postData) => {
setOptions({
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(postData),
});
};
useEffect(() => {
const controller = new AbortController();
const fetchData = async (fetchOptions) => {
setIsPending(true);
try {
const res = await fetch(url, {
...fetchOptions,
signal: controller.signal,
});
if (!res.ok) {
throw new Error(res.statusText);
}
const json = await res.json();
setIsPending(false);
setData(json);
setError(null);
} catch (err) {
if (err.name === "AbortError") {
console.log("the fetch was aborted");
} else {
setIsPending(false);
setError("Could not fetch the data");
}
}
};
if (method === "GET") {
fetchData();
}
if (method === "POST" && options) {
fetchData(options);
}
return () => {
controller.abort();
};
}, [url, options, method]);
return { data, isPending, error, postData };
};
TaskList.js
import { useFetch } from "../hooks/useFetch";
import { useFrequency } from "../hooks/useFrequency";
//images
import Dot from "../assets/icon-ellipsis.svg";
// styles
import "./TaskList.scss";
export default function TaskList() {
// const [url, setUrl] = useState("http://localhost:3000/stats");
const { data: stats, isPending, error } = useFetch("/data.json");
const { frequency } = useFrequency();
const urlDot = "#";
return (
<div className="main__inner">
{isPending && <div>Loading stats...</div>}
{error && <div>{error}</div>}
<ul className="main__task-list">
{stats &&
stats.map((stat) => (
<li
className={
stat.title === "Self Care"
? "main__task-item selfcare"
: `main__task-item ${stat.title.toLowerCase()}`
}
key={stat.id}
>
<div className="main__task-item-container">
<h3 className="main__task-title">{stat.title}</h3>
<a href={urlDot} className="main__task-dot">
<img src={Dot} alt="more details" />
</a>
<span className="main__task-current">
{/* {frequency === "daily"
? stat.timeframes.daily.current
: frequency === "weekly"
? stat.timeframes.weekly.current
: stat.timeframes.monthly.current}
hrs */}
{stat.timeframes[frequency].current}hrs
</span>
<span className="main__task-previous">
{frequency === "daily"
? "Yesterday"
: frequency === "weekly"
? "Last Week"
: "Last Month"}{" "}
-{" "}
{
/* {frequency === "daily"
? stat.timeframes.daily.previous
: frequency === "weekly"
? stat.timeframes.weekly.previous
: stat.timeframes.monthly.previous} */
stat.timeframes[frequency].previous
}
hrs
</span>
</div>
</li>
))}
</ul>
</div>
);
}
You can configure the Environment variable in Vercel after you have imported your site from GitHub or another repository.
You can set it on the Configure Project window by clicking the Environment Variable Tab. See the image below
I try to do Load More on a list of data as written below:
import React, { useState, useEffect } from "react";
import { render } from "react-dom";
import axios from "axios";
import "./style.css";
const App = () => {
const LIMIT = 2;
const [data, setData] = useState([]);
const [isLoading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const loadData = async (skip = 1, limit = LIMIT) => {
const URL = "https://reqres.in/api/users";
const headers = {
"Content-Type": "application/json",
Accept: "application/json"
};
const params = {
page: skip,
per_page: limit
};
const a = await axios.get(URL, { params, headers });
// const b = [...new Set([...data, ...a.data.data])]; <-- setting this will thrown error
setData(a.data.data);
setLoading(false);
};
useEffect(() => {
setLoading(true);
loadData(page);
}, [page]);
useEffect(() => {
console.log("page", page, "data", data.length);
}, [page, data]);
const doReset = evt => {
evt.preventDefault();
setPage(1);
};
const doLoadMore = evt => {
evt.preventDefault();
setPage(page + 1);
};
return (
<div className="container">
<h1>Listing</h1>
<button className="btn text-primary" onClick={evt => doReset(evt)}>
Reset
</button>
<button className="btn text-primary" onClick={evt => doLoadMore(evt)}>
Load More
</button>
{isLoading && <p>Loading..</p>}
{!isLoading && (
<ul>
{data.map(a => (
<li key={a.id}>
{a.id}. {a.email}
</li>
))}
</ul>
)}
</div>
);
};
render(<App />, document.getElementById("root"));
a fully working example in here.
i think this code should be working, but is not.
const a = await axios.get(URL, { params, headers });
const b = [...new Set([...data, ...a.data.data])];
setData(b);
so please help, how to do Load More in React Hooks?
after a few try, i think this is the best thing i can do. make the code working but also not let the compiler warning:
import React, { useState, useEffect, useCallback } from "react";
import axios from "axios";
import Navbar from "./Navbar";
const App = () => {
const LIMIT = 2;
const [tube, setTube] = useState([]);
const [data, setData] = useState([]);
const [isLoading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const loadData = useCallback(
async (limit = LIMIT) => {
setLoading(true);
const URL = "https://reqres.in/api/users";
const headers = {
"Content-Type": "application/json",
Accept: "application/json"
};
const params = {
page,
per_page: limit
};
const a = await axios.get(URL, { params, headers });
if (!a.data.data) {
return;
}
setData(a.data.data);
setLoading(false);
},
[page]
);
useEffect(() => {
if (!isLoading) {
return;
}
setTube([...new Set([...tube, ...data])]);
}, [data, isLoading, tube]);
useEffect(() => {
loadData();
}, [loadData]);
useEffect(() => {
console.log("page", page, "data", data.length);
}, [page, data]);
const doLoadMore = evt => {
evt.preventDefault();
setPage(page + 1);
};
return (
<>
<Navbar />
<main role="main" className="container">
<div className="starter-template text-left">
<h1>Listing</h1>
<button className="btn text-primary" onClick={evt => doLoadMore(evt)}>
Load More
</button>
<ul>
{tube &&
tube.map(a => (
<li key={a.id}>
{a.id}. {a.email}
</li>
))}
</ul>
{isLoading && <p>Loading..</p>}
</div>
</main>
</>
);
};
export default App;
also i found, it could be much easier just apply this eslint-disable-next-line react-hooks/exhaustive-deps to let the compiler ignore the warning. something like this.
useEffect(() => {
setConfig({...config, params: {...params, skip}});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [skip]);
for information can be found on this:
how-to-fix-missing-dependency-warning-when-using-useeffect-react-hook
https://stackoverflow.com/a/55844055/492593
react #14920
I got your example to work by changing to this:
const b = [...data, ...a.data.data];
setData(b);