Using ApexChart I manage to display the data from a local const, but I can't display this data fetching with axios.
I can't get data result, console.log or error
File that manage the chart option
export const chartData = {
height: 460,
type: 'rangeBar',
id: 'range-chart',
options: {
chart: {
... },
series: []
};
File that display the chart
const MainChart = ({ isLoading }) => {
const [rawdata, setRawdata] = useState([]);
useEffect(() => {
const loadData = async () => {
const res = await axiosConfig.get(`/<ENDPOINT>/`)
.then(res => {
console.log('data load', res.data.record) //is showing nothing
setRawata([{
data: res.data.record
}])
}
)
}
}, [])
return (
<>
{isLoading ? (
<p>Loading...</p>
) : (
<Grid container spacing={0}>
<Grid item xs={12} mt={5}>
<Chart
options={{
...chartData.options
}}
series={[{
data: rawdata
}]}
/>
</Grid>
</Grid>
)}
</>
);
I only get options. I don't have any data result, no console log and no console error
I'm answering my own question in case help to others.
After investigating I solved my own question.
In React ApexChart you can pass all the values from one file, or split options from other values.
In the file that manage the options
export const chartData = {
// height: 460,
// type: 'rangeBar',
// id: 'range-chart',
options: {
chart: {
... },
series: []
};
Chart component. Here you pass options, series and the 3 values that you commented on the options files.
<Chart
id="range-chart"
type="rangeBar"
height={460}
options={{
...chartData.options
}}
series={
data
}
/>
The fetch issue is not related with ApexChart, but as you can guess there was a bad syntax.
const [data, setData] = useState([])
useEffect(() => {
loadData()
}, [])
const loadData = async () => {
const res = await axiosConfig.get(`<ENDPOINT>`)
setData(res.data.record)
}
Related
I am trying to render an array of data on the UI. So far, I have been able to console.log the array but have not been able to display it on a table.
I am currently using Axios to retrieve the data from the back-end and am trying to render the data in Bootstrap Table. My initial issue was that I needed to assign each child in the list a unique key prop. Now that this error is gone, I still cannot see the data in the UI.
Invite table component (InviteTable.js):
import React, { useState, useEffect } from 'react';
import Axios from 'axios';
import BootstrapTable from 'react-bootstrap-table-next';
import PaginationFactory from 'react-bootstrap-table2-paginator';
import ToolkitProvider, {Search} from 'react-bootstrap-table2-toolkit/dist/react-bootstrap-table2-toolkit';
import Spinner from 'react-bootstrap/Spinner';
const InviteTable = () => {
const [invites, setInvites] = useState([]);
const [loading, setLoading] = useState(false);
const { SearchBar } = Search;
const url = "http://localhost:5000/api/get";
//Define columns
const columns = [
{ dataField: "Id", text: "Id", headerStyle: () => {return { width: "10%" };} },
{ dataField: "Code", text: "Invite Code", headerStyle: () => {return { width: "10%" };} },
{ dataField: "Recipient", text: "Recipient Email", headerStyle: () => {return { width: "35%" };} },
{ dataField: "Created", text: "Date Sent", headerStyle: () => {return { width: "35%" };} },
];
//GET and set data
useEffect(() => {
Axios.get(url).then(result => {
setInvites(result.data);
console.log(result.data);
setLoading(true);
})
.catch(function(error) {
console.log(error);
});
}, []);
return (
<div>
{loading ? (
<ToolkitProvider keyField="Id" data={invites} columns={columns} search>
{(props) => (
<div>
<SearchBar {...props.searchProps} />
<BootstrapTable
{...props.baseProps}
pagination={PaginationFactory()}
/>
</div>
)}
</ToolkitProvider>
) : (
<Spinner animation="border" />
)}
</div>
)
};
export { InviteTable }
Console:
Output of console.log
I have resolved this issue. The problem lay in the server file that was running the query to return the invites.
I am quite new to this so am still in the process of learning as I go.
Initially I was sending the entire result to the front end which was causing issues:
request.query('select * from Invites;', (err, result) => {
// console log error if there is one
if (err) console.log(err);
// send records as a response
res.send(result);
});
To resolve this, I specified that the recordset should be sent to the front end:
request.query('select * from Invites;', (err, result) => {
// console log error if there is one
if (err) console.log(err);
// send records as a response
res.send(result.recordset);
});
Here is the solution, first check your data if it's in the object then convert it into an array. and make sure your columns' dataField name same as your database parameters
const [post, setPost] = useState([]);
then(response => {
const data = response.data;
const arr = Object.entries(data); //convert object into array
arr.forEach(([key, value]) => {
setPost(oldArray => [...oldArray, value]); // Assign Data toTable
});
})
<BootstrapTable keyField='id' data={post} columns={columns} pagination={paginationFactory()}/>
I have an array of data resembling the following:
data = [{name: 'A', data: 1}, {name: 'B', data: 2}]
I also have code resembling the following:
function ReportComponent({ data }) {
return data.map((datum) => (
<Typography>
{datum.name}: {datum.data}
</Typography>
));
}
which is called in
function ReportBox({ component }) {
const { data } = useFetchHook(component.urls)
// data returns exactly as expected, an array of objects
return (
<Box>
<Typography>
{component.title}
</Typography>
{data !== null && <ReportComponent data={data} />}
</Box>
);
}
My issue is, when I run the application, I only get one output from my data (when I console.log(data) it returns the data I showed above), either
A: 1 OR B:2. I expect there to be both present in the component. Any advice?
---- Update ----
useFetch function
import { useState, useEffect } from 'react';
function useFetch(urls) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
let i = urls.length - 1;
const result = [];
while (i >= 0) {
const abortCont = new AbortController();
console.log(`url ${i}`);
console.log(urls[i]);
fetch(urls[i], { signal: abortCont.signal }, { mode: 'cors' })
.then((res) => {
if (!res.ok) {
console.log('something went wrong with the data fetch');
}
return res.json(); // why?
})
.then((data) => {
result.push(data);
setData(result);
})
.catch((err) => {
if (err.name === 'AbortError') {
console.log('aborted');
} else {
setError(err.message);
}
});
i -= 1;
}
}, [urls]);
// console.log(data);
return { data, error };
}
export default useFetch;
--- Update DashBox ---
mport { Box, Grid, Container, Typography } from '#mui/material';
import ReportBox from './ReportBox';
function DashBox({ components }) {
// console.log(components);
return (
<Grid
item
columns={5}
sx={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-evenly',
alignItems: 'stretch',
marginTop: '20px',
marginLeft: '5px'
}}
>
{components.map((component) => (
<ReportBox component={component} />
))}
</Grid>
);
}
export default DashBox;
--- Update Page ---
export default function Page() {
const optionsFilter= [
'A',
'B',
'C'
];
const [filter, setFilter] = useState('A');
const componentsPage = [
{
title: 'One',
urls: [
`http://localhost:9000/page1?filter=${filter}`,
`http://localhost:9000/page2?filter=${filter}`
]
}
];
const componentsPageGraphs = [
{
title: 'OneGraph',
urls: [
`http://localhost:9000/page1?filter=${filter}`,
`http://localhost:9000/page2?filter=${filter}`
]
}
];
return (
<Page title="Page">
<Container>
<Typography variant="h4" sx={{ mb: 5 }}>
Page
</Typography>
<Container marginBottom="10px">
<Typography marginLeft="5px" variant="h5">
Filters
</Typography>
<Grid
columns={5}
sx={{
display: 'flex',
flexDirection: 'row',
alignItems: 'stretch',
marginTop: '10px',
marginLeft: '5px',
justifyContent: 'space-evenly'
}}
>
<Grid item sx={{ pr: 5 }}>
<DropDown
options={optionsFilter}
title="Filter Type"
setData={setFilter}
data={filter}
key="one"
/>
</Grid>
</Grid>
</Container>
<br />
<Box
container
sx={{ border: 2 }}
marginLeft="20px"
pr="20px"
pb="20px"
pl="20px"
width="100%"
>
<Typography variant="h3">Page Dashboard</Typography>
<DashBox components={componentsPage} />
</Box>
<Grid container spacing={2} marginTop="20px">
{componentsPageGraphs.map((component) => (
<Grid item xs={6}>
<Typography>{component.title}</Typography>
<LineChart xtype="category" urls={component.urls} />
</Grid>
))}
</Grid>
</Container>
</Page>
);
}
---- Update again with the suggested fetch, unfortunately still overwriting ---
import { useState, useEffect, useRef } from 'react';
const sameContents = (array1, array2) =>
array1.length === array2.length && array1.every((value, index) => value === array2[index]);
function useFetch(urls) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const urlsRef = useRef(null);
if (!urlsRef.current || !sameContents(urlsRef.current, urls)) {
urlsRef.current = urls.slice();
}
useEffect(() => {
const results = [];
if (!urlsRef.current) {
return;
}
const controller = new AbortController();
const { signal } = controller;
Promise.all(
urlsRef.current.map((url) => {
fetch(url, { signal, mode: 'cors' })
.then((res) => {
if (!res.ok) {
console.log('http issue');
}
return res.json();
})
.then((data) => {
if (!signal.aborted) {
results.push(data);
setData(results);
setError(null);
}
})
.catch((error) => {
if (signal.aborted) {
return;
}
setData(null);
setError(error);
});
return () => {
controller.abort();
};
})
);
}, [urlsRef.current]);
return { data, error };
}
export default useFetch;
Stack Snippet:
const {useState, useEffect} = React;
// Fake Typography component
const Typography = ({children}) => <div>{children}</div>;
// Fake Box component
const Box = ({children}) => <div>{children}</div>;
// Fake fetch hook
function useFetchHook(urls) {
const [data, setData] = useState(null);
useEffect(() => {
setTimeout(() => {
setData([
{name: "One", data: "Data for 'One'"},
{name: "Two", data: "Data for 'Two'"},
{name: "Three", data: "Data for 'Three'"},
]);
}, 500);
}, []);
return {data};
}
function ReportComponent({ data }) {
return data.map((datum) => (
<Typography>
{datum.name}: {datum.data}
</Typography>
));
}
function ReportBox({ component }) {
const { data } = useFetchHook(component.urls)
// data returns exactly as expected, an array of objects
return (
<Box>
<Typography>
{component.title}
</Typography>
{data !== null && <ReportComponent data={data} />}
</Box>
);
}
ReactDOM.render(<ReportBox component={{urls: [], title: "Example"}} />, document.getElementById("root"));
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.development.js"></script>
Your Page component creates a new componentsPage object with new urls arrays in the components every time it renders. Those new urls arrays are ultimately passed to useFetch (aka useFetchHook), where you have this structure:
function useFetch(urls) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
useEffect(() => {
// ...code that fetches and sets `data`/`error`...
}, [urls]);
// console.log(data);
return { data, error };
}
That means that every time the urls parameter changes value (the old value isn't === the new value), it will repeat the fetch and update data or error.
There are various issues with the hook as well, the primary problem being that it does asynchronous work (a series of fetch calls) but doesn't check to be sure that the results its getting aren't outdated (because urls changed). More on that in a moment.
Since the urls arrays are recreated every time, useFetch does the fetches again every time, because no array is ever === any other array, even if they have the same contents:
console.log(["1", "2", "3"] === ["1", "2", "3"]); // false
So you need to:
Have useFetch only start a new series of fetches when the URLs really change. If it's given a new array with the same contents, it shouldn't do a new set of fetches.
useFetch should abort the fetches that are in progress if it's about to get a new set of urls, and shouldn't use the previous results if that's happened.
You seem to have started on #2 by using an AbortController, but nothing every called its abort method, so it didn't do anything.
Here's a version of useFetch that handles both of those things, see the comments:
const sameContents = (array1, array2) => {
return array1.length === array2.length &&
array1.every((value, index) => value === array2[index]);
};
function useFetch(urls) {
const [data, setData] = useState(null);
const [error, setError] = useState(null);
const urlsRef = useRef(null); // A place to keep the URLs we're handling
if (!urlsRef.current || // Mounting, or
!sameContents(urlsRef.current, urls) // Called after mount with *different* URLs
) {
// Remember these URLs
urlsRef.current = urls.slice();
}
useEffect(() => {
if (!urlsRef.current) {
// Nothing to do
return;
}
// Use the same controller and signal for all the fetches
const controller = new AbortController();
const {signal} = controller;
// Use `Promise.all` to wait for all the fetches to complete (or one
// of them to fail) before setting `data`.
Promise.all(urlsRef.current.map(url =>
// Note: You had `{ mode: "cors" }` on its own as a third argument,
// but it should have been part of the second argument (`fetch`
// only takes two).
fetch(url, {signal, mode: "cors"})
.then(res => {
if (!res.ok) {
// HTTP error
throw new Error(`HTTP error ${res.status}`);
}
// HTTP okay, read the body of the response and parse it
return res.json();
})
))
.then(data => {
// Got all the data. If this set of results isn't out of date,
// set it and clear any previous error
if (!signal.aborted) {
setData(data);
setError(null);
}
})
.catch(error => {
// Do nothing if these results are out of date
if (signal.aborted) {
return;
}
// Clear data, set error
setData(null);
setError(error);
});
// Return a cleanup callback to abort the set of fetches when we get
// new URLs.
return () => {
controller.abort();
};
}, [urlsRef.current]); // <=== Use this instead of `urls`
return { data, error };
}
That's a sketch, I won't be surprised if you need to make small tweaks to it, but it should get you going the right way.
I am trying to display a collection from MongoDB inside a React webpage, with no success so far.
I got the data as json in the backend but I am not sure how to bring it to frontend.
The data I get looks like this:
{
_id: 60d5ee8d9f3b772030ae319c,
username: 'test',
email: 'test#test.com',
password: '$2a$10$5F8k.qfP3Bi7vlLKuOxcneejlsnxB4cN1SerV7tFkAS6v2E.YhBqK',
__v: 0
}
This is where I am trying to display the data:
import { useState, useEffect } from "react";
import axios from "axios";
import "./PrivateScreen.css";
const PrivateScreen = ({ history }) => {
const [error, setError] = useState("");
const [privateData, setPrivateData] = useState("");
useEffect(() => {
if (!localStorage.getItem("authToken")) {
history.push("login");
}
const fetchPrivateDate = async () => {
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem("authToken")}`,
},
};
try {
const { data } = await axios.get("/api/private", config);
setPrivateData(data.data);
} catch (error) {
localStorage.removeItem("authToken");
setError("You are not authorized please login");
}
};
fetchPrivateDate();
}, [history]);
const logoutHandler = () => {
localStorage.removeItem("authToken");
history.push("/login");
};
return error ? (
<span className="error-message">{error}</span>
) : (
<>
<div style={{ background: "green", color: "white" }}>{privateData}</div>
<button onClick={logoutHandler}>Logout</button>
<div>DISPLAY THE DATA HERE<div/>
</>
);
};
export default PrivateScreen;
The actual data I will get will be big, and I know I have to map it, but first I need to get to this step :D.
Could someone please give me a few hints, and sorry if I didn't give enough details, I will try and give more information if you guys require it.
Thank you, have a great day!
As you are storing the data in the privateData state, you can access that data in the whole function, here is an example of what you can do.
return error ? (
<span className="error-message">{error}</span>
) : (
<>
<div style={{ background: "green", color: "white" }}>{privateData}</div>
<button onClick={logoutHandler}>Logout</button>
<div>email: {privateData.email}, username: {privateData.username}<div/>
</>
);
or if privateData is an array you can show the data helping map, here is another example:
return error ? (
<span className="error-message">{error}</span>
) : (
<>
<div style={{ background: "green", color: "white" }}>{privateData}</div>
<button onClick={logoutHandler}>Logout</button>
<div>{privateData.map((el) => {
return (
el.username
)
})}<div/>
</>
);
i'am learning TS yet and I trying to create an application where I get data from API, show results and if someone click on item, it shows a modal with more details, but i'am trouble cause basically my component doesn't render... Look at my code =) !
import IMovie from "../../models/movie.model";
import Modal from "../modal/Modal";
import "./style";
import {
ResultsBody,
ResultsContainer,
TitleResult,
MovieStats,
MovieCover,
MovieStatsDescription,
} from "./style";
interface ISearch {
search?: string;
}
const URL =
"#";
const Results = ({ search }: ISearch) => {
const [data, setData] = React.useState<IMovie[]>([]);
const [currentPage, setCurrentPage] = React.useState(1);
const [dataPerPage] = React.useState(10);
async function getData() {
const response: AxiosResponse<any> = await axios.get(URL);
setData(response.data.results);
}
React.useEffect(() => {
getData();
}, []);
const indexLastData = currentPage * dataPerPage;
const indexFirstData = indexLastData - dataPerPage;
const currentData = data.slice(indexFirstData, indexLastData);
const paginate = (pageNumber: number) => setCurrentPage(pageNumber);
const filteredData = data.filter((results) => {
return results.title.toLowerCase().includes(search!.toLocaleLowerCase());
});
return (
<>
<ResultsContainer>
<TitleResult>
<span>Personagem</span>
<span>Sinopse</span>
<span>Data</span>
</TitleResult>
{!search
? currentData.map((item) => (
<ResultsBody
key={item.id}
// onClick={() => {
// selectedMovie(item);
// }}
>
<MovieCover
src={`https://image.tmdb.org/t/p/w185${item.poster_path}`}
alt="poster"
/>
<MovieStats style={{ fontWeight: `bold` }}>
{item.title}
</MovieStats>
<MovieStatsDescription>{item.overview}</MovieStatsDescription>
<MovieStats>{item.release_date}</MovieStats>
</ResultsBody>
))
: filteredData.map((item) => (
<ResultsBody key={item.id}>
<MovieCover
src={`https://image.tmdb.org/t/p/w185${item.poster_path}`}
alt="poster"
/>
<MovieStats style={{ fontWeight: `bold` }}>
{item.title}
</MovieStats>
<MovieStatsDescription>{item.overview}</MovieStatsDescription>
<MovieStats>{item.release_date}</MovieStats>
</ResultsBody>
))}
</ResultsContainer>
<Modal data={data} /> //HERE IS WHERE I'AM CALLING MY MODAL, I want to pass data here
<Pagination
dataPerPage={dataPerPage}
totalData={data.length}
paginate={paginate}
currentPage={currentPage}
/>
</>
);
};
export default Results;
This is my MODAL component
import React from "react";
import { ModalContainer } from "./style";
import IMovie from "../../models/movie.model";
interface IData {
data: IMovie[];
}
const Modal = ({ data }: IData) => {
console.log(data);
return <ModalContainer>{data.title}</ModalContainer>; //HERE IS NOT WORKING
};
export default Modal;
As you can see guys, I can show all results on console.log, but when I put inside the return the log says ''TypeError: Cannot read property 'title' of undefined''
If someone could help me I'd really appreciate! Thanks a lot =)
Movie vs Array
You are getting the error
'Property 'title' does not exist on type 'IMovie[]'. TS2339
in your Modal component because data is an array of movies. An array doesn't have a title property.
You want the modal to show one movie, so you should only pass it one movie.
interface IData {
data: IMovie;
}
Current Selection
Changing the IData interface fixes the issues in Modal, but creates a new error in Results because we are still passing an array. The correct prop is the data for the movie that was clicked. What movie is that? We need to use a useState hook in order to store that data.
Depending on where you control the open/closed state of the Modal, you may also want to pass an onClose callback that clears the selected movie state.
the state:
const [selected, setSelected] = React.useState<IMovie | null>(null); // is a movie or null
in the movie:
onClick={() => setSelected(item)}
the modal:
{selected === null || (
<Modal
data={selected}
onClose={() => setSelected(null)}
/>
)}
Avoid Duplicated Code Blocks
You are rendering a movie the same way whether it's from currentData or filteredData, so we want to combine those. We could create a shared renderMovie callback or ResultsMovie component to use in both loops, but I think we can actually handle it higher up and just have one loop.
You also want your pagination to reflect the pages of just the matching movies when we are filtering based on a search.
// the matchingMovies is a filtered array when there is a search, or otherwise the entire array
const matchingMovies = search
? data.filter((result) =>
result.title.toLowerCase().includes(search.toLowerCase())
)
: data;
const indexLastData = currentPage * dataPerPage;
const indexFirstData = indexLastData - dataPerPage;
const paginate = (pageNumber: number) => setCurrentPage(pageNumber);
// total for the pagination should be based on matchingMovies instead of data
const totalData = matchingMovies.length;
// make the currentData from the matchingMovies
const currentData = matchingMovies.slice(indexFirstData, indexLastData);
There might be some bugs or potential additional improvements but I can't actually run this without your components :)
const Results = ({ search }: ISearch) => {
const [data, setData] = React.useState<IMovie[]>([]);
const [currentPage, setCurrentPage] = React.useState(1);
const [dataPerPage] = React.useState(10);
const [selected, setSelected] = React.useState<IMovie | null>(null); // is a movie or null
async function getData() {
const response: AxiosResponse<any> = await axios.get(URL);
setData(response.data.results);
}
React.useEffect(() => {
getData();
}, []);
// the matchingMovies is a filtered array when there is a search, or otherwise the entire array
const matchingMovies = search
? data.filter((result) =>
result.title.toLowerCase().includes(search.toLowerCase())
)
: data;
const indexLastData = currentPage * dataPerPage;
const indexFirstData = indexLastData - dataPerPage;
const paginate = (pageNumber: number) => setCurrentPage(pageNumber);
// make the currentData from the matchingMovies
const currentData = matchingMovies.slice(indexFirstData, indexLastData);
return (
<>
<ResultsContainer>
<TitleResult>
<span>Personagem</span>
<span>Sinopse</span>
<span>Data</span>
</TitleResult>
{currentData.map((item) => (
<ResultsBody key={item.id} onClick={() => setSelected(item)}>
<MovieCover
src={`https://image.tmdb.org/t/p/w185${item.poster_path}`}
alt="poster"
/>
<MovieStats style={{ fontWeight: `bold` }}>{item.title}</MovieStats>
<MovieStatsDescription>{item.overview}</MovieStatsDescription>
<MovieStats>{item.release_date}</MovieStats>
</ResultsBody>
))}
</ResultsContainer>
{selected === null || (
<Modal data={selected} onClose={() => setSelected(null)} />
)}
<Pagination
dataPerPage={dataPerPage}
totalData={matchingMovies.length}
paginate={paginate}
currentPage={currentPage}
/>
</>
);
};
interface ModalProps {
data: IMovie;
onClose: () => void;
}
const Modal = ({ data, onClose }: ModalProps) => {
console.log(data);
return <ModalContainer>{data.title}</ModalContainer>;
};
today i decided to update the dependencies of my react project and my component Home didn't work anymore, i'm actually working with a apollo client and apollo react hooks, this is mi Home component file:
function Home(props) {
const {
loading,
data: { getPosts: posts }
} = useQuery(FETCH_POSTS_QUERY);
return (
<Grid columns={3} stackable={true} className={loading ? 'loading' : ''}>
<Grid.Row className='page-title'>
<h1>Recent Posts</h1>
</Grid.Row>
<Grid.Row>
{user && (
<Grid.Column>
<PostForm user={user} />
</Grid.Column>
)}
{loading ? (
<Loading />
) : (
posts &&
posts.map(post=> (
<Grid.Column key={post._id} style={{ marginBottom: 20 }}>
<PostCard post={post} />
</Grid.Column>
))
)}
</Grid.Row>
</Grid>
);
}
and i'm getting this error in the browser:
"TypeError: Cannot read property 'getPosts' of undefined"
i'm trying to fix it with this little code variation:
function Home(props){
let posts = '';
const { user } = useContext(AuthContext);
const { loading, data } = useQuery(FETCH_POSTS_QUERY);
if (data) {
posts = data.getPosts;
}
And everything works fine, but if i add a new Post updating the apollo cache, that cache update correctly with old posts and new post, but the frontend didn't show it, only show old posts until i refresh the page manually.
Edit:
This is the code from the PostForm component, i updated the Home component too adding the PostForm:
function PostForm(props) {
const { values, handleChange, handleSubmit } = useForm(createPostCallback, {
title: 'Example Title',
body: ''
});
const [createPost] = useMutation(CREATE_POST_MUTATION, {
variables: values,
update(dataProxy, result) {
const data = dataProxy.readQuery({
query: FETCH_POSTS_QUERY
});
data.getPosts = [result.data.createPost, ...data.getPosts];
dataProxy.writeQuery({
query: FETCH_POSTS_QUERY,
data
});
values.body = '';
}
});
function createPostCallback() {
createPost();
}
Any idea how to fix the first code issue?
Thanks in advance mates!
I fixed same error with defining data as an object {}
just changed the below code by adding = {}
const {
loading,
data: { getPosts: posts } = {}
} = useQuery(FETCH_POSTS_QUERY);
Queries for read and write cache in apollo works in a inmutable way. In order to do that, you have to use a new variable, you're using data to write the cache.
Try doing this:
const [createPost] = useMutation(CREATE_POST_MUTATION, {
variables: values,
update (proxy, result) {
const data = proxy.readQuery({
query: FETCH_POSTS_QUERY
})
const new_post = result.data.createPost //here's the new var
proxy.writeQuery({
query: FETCH_POSTS_QUERY,
data: { getPosts: [new_post, ...data.getPosts] } // here you're using that var to write the cache
})
values.body = ''
}
})
I would take your if statement and set that inside a useEffect so it checks
if data is a truthy onLoad and so you can sync it to whenever data changes.
const [posts, setPosts] = useState([]);
useEffect(() => {
if (data) {
setPosts(data.getPosts);
}
},[data])
if (posts.length === 0) {
return <h3>No posts as of yet</h3>
}