How to refactore Supabase CRUD Functions from a Sveltekit Store efficiently - sveltekit

I been trying to get the hang of supabase as a new dev and it took a while to understand.
I don't think my code is efficient event though it works. Any help would be greatly appreciated!
My current store looks like this
import { writable } from 'svelte/store';
import supabase from '$lib/db';
export const todos = writable([]);
export const task = writable('');
export const price = writable();
export const getTodos = async () => {
let { data, error } = await supabase.from('todos').select('*');
if (!error) {
todos.set(data);
}
};
export const deleteTodos = async (id) => {
const { data, error } = await supabase.from('todos').delete().match({ id });
if (!error) {
await getTodos();
}
};
export const insertTodos = async (task, price) => {
const { data, error } = await supabase
.from('todos')
.insert([{ task, isCompleted: false, price }]);
if (!error) {
await getTodos();
lol.set(true);
}
};
export const updatePost = async (task, price, id) => {
const { data, error } = await supabase.from('todos').update({ task, price }).match({ id });
if (!error) {
await getTodos();
}
};
export const toggle = async (id, isCompleted) => {
const { data, error } = await supabase
.from('todos')
.update({ isCompleted: !isCompleted })
.match({ id });
if (!error) {
await getTodos();
}
};

Not sure what you're exactly trying to achieve, but you could just make those CRUD functions part of the store:
// store.js
function todoStore() {
const store = writable([])
function deleteTodo() { ... }
function insertTodo() { ... }
// ...and all your other methods
return {
subscribe: store.subscribe,
insert: insertTodo,
delete: deleteTodo,
}
}
export default todoStore()
You can use this store then in your component like this:
import todos from 'store.js'
// Get value of store
$todos
// Delete todo
todos.delete(id)
// Insert todo
todos.insert({ ... })
Note that you can give the store constructor a function that executes when it receives its first subscriber, see docs. This is helpful to init a store or remove any listeners when the store is being destroyed.
Svelte's store contract is important to understand: as long as you have a subscribe method, you can design your stores in any way you like.

Related

Change data on two different querys from React Query

so im doing a little application of a pokedex utilizing the pokeapi to test react query, what i am trying to do is first fetch the data on mount and after that, if i use the search button the data already fetched to change something like this
fetch on mount
and the search
search fetch
something easy to do with useState but i am having problems with react query
i got something like this
pokedex
at the mount of the component i have this
export const fetchPokemon = async (URL: string) => {
const result = await axios.get<pokemon>(URL);
return result;
};
export const fetchPokemons = async () => {
const URL = "https://pokeapi.co/api/v2/pokemon?limit=20&offset=0s";
const { data } = await axios.get<pokemons>(URL);
const result = await Promise.all(
data.results.map(async (pokemon) => {
return fetchPokemon(pokemon.url);
})
);
return result;
};
export const useAllPokemons = () => {
return useQuery({
queryKey: ["pokemons"],
queryFn: fetchPokemons,
});
};
const { data, isLoading } = useAllPokemons();
works great but now i want to search pokemons with a search button like in the image and to replace the initial data that i already fetch so only the data that i searched appears so i did this
export const fetchAllPokemons = async () => {
const URL = "https://pokeapi.co/api/v2/pokemon?limit=100";
const { data } = await axios.get<pokemons>(URL);
const result = await Promise.all(
data.results.map(async (pokemon) => {
return fetchPokemon(pokemon.url);
})
);
return result;
};
let { data, refetch } = useQuery({
queryKey: ["pokemons"],
queryFn: fetchAllPokemons,
enabled: false,
select: (data) => {
const pokemonData = data.map((pokemon) => {
if (pokemon.data.name.startsWith("char")) {
return pokemon;
}
});
return pokemonData;
},
});
<button
onClick={() => {
refetch();
}}
>
asd
</button>
and nothing happens, but when i open the console the data is changing but then again returns to the initial fetch
I guess you should use the onSuccess:
const {data} = useQuery("fetchData", fetchData, {
onSuccess: (data) => {
// do something with your data and return
}
});

How to integrate redux into firebase firestore?

I am able to get the data in categoryMap from firebase firestore but I want to save the data in a state and then share that state between components using redux toolkit. Should I create action creators in the code below or should I use react's useState hook and then apply redux on top of that?
Here is the code snippet of the data I get from firestore:
export const getCategoriesAndDocuments = async () => {
const collectionRef = collection(db, 'categories');
const q = query(collectionRef);
const querySnapshot = await getDocs(q);
const categoryMap = querySnapshot.docs.reduce((acc, docSnapshot) => {
const { title, items } = docSnapshot.data();
acc[title.toLowerCase()] = items;
return acc;
}, {});
};
You can directly store your data in your redux store. Since redux store is A store holds the whole state tree of your application you don't need to add an extra state.
I know this is an old question, but if anyone ends up here looking for an answer:
Besides what was suggested by Tolunay Ă–zdemir, you can also use RTK Query to fetch data from Firestore. This way you can benefit from its caching capabilities and use...Query, use...Mutation hooks if you want to use them.
You can do something like the following, using fakeBaseQuery() as baseQuery, and queryFn instead of regular query:
import { createApi, fakeBaseQuery } from '#reduxjs/toolkit/query/react';
import { collection, doc, getDocs } from 'firebase/firestore';
import { db } from '../../firebase';
export const getCategoriesAndDocuments = async () => {
const collectionRef = collection(db, 'categories');
const q = query(collectionRef);
const querySnapshot = await getDocs(q);
const categoryMap = querySnapshot.docs.reduce((acc, docSnapshot) => {
const { title, items } = docSnapshot.data();
acc[title.toLowerCase()] = items;
return acc;
}, {});
return categoryMap;
};
export const api = createApi({
baseQuery: fakeBaseQuery(),
endpoints: (builder) => ({
fetchData: builder.query({
async queryFn() {
try {
const data = await getCategoriesAndDocuments();
return { data };
} catch (error) {
console.error(error.message);
return { error: error.message };
}
},
}),
}),
});
export const { useFetchDataQuery } = api;
I wrote a blog post if you'd like more details.

Unable to test-cover the catch block in my react code

I'm writing test cases using react testing library. Having issues covering the catch block which is failing my build.
Below is my component code with a simple button click handler calling the api.
TestComponent.js
import Button from '#material-ui/core/Button';
import MyApiFile from '../api/MyApiFile';
function someFunction(setIsSuccess, setIsError) {
setIsSuccess(false);
setIsError(false);
const { request } = MyApiFile.getThisOrThat();
request.then(({ status, response }) => {
someOtherFunction();
})
.catch((err) => {
setIsSuccess(true);
setIsError(true);
});
}
function TestComponent() {
const [isSuccess, setIsSuccess] = useState(false);
const [isFail, setIsFail] = useState(false);
const handleSave = () => {
someFunction(setIsSuccess, setIsFail)
};
return(
<Button data-testid="save-btn" onClick={ handleSave }>
Save
</Button>
)
}
export default TestComponent;
The API urls are generated and returned by the respective static functions of the MyApiFile class
MyApiFile.js
import axios, { CancelToken } from 'axios';
import urljoin from 'url-join';
import URI from 'urijs';
export default class MyApiFile {
static getURI(path){
const { config = {} } = window;
const { apiBasePath = '/' } = config;
const apiUri = new URI(urljoin(apiBasePath, path));
return apiUri;
}
static apiRequest(config){
const source = CancelToken.source();
config.cancelToken = source.token;
const request = axios.request(config)
return { request, source };
}
static getThisOrThat(param, blah) {
const apiUrl = MyApiFile.getURI(`/some/path/${param}`);
return MyApiFile.apiRequest({
method: 'post',
url: String(apiUrl),
data: blah
});
}
}
Below are the three approaches (marked by -->) that I tried after researching on internet,
TestComponent.test.js
beforeEach(()=> {
act(() => render(<TestComponent />));
})
it('should do so n so' , () => {
const mockError = { error: 'something went wrong' };
--> jest.mock('../MyApiFile', ()=>({
getThisOrThat: jest.fn( Promise.reject(mockError))
}))
--> jest.spyOn(MyApiFile, 'getThisOrThat').mockRejectedValue(mockError)
const save = screen.getByTestId('save-button');
await act(() => {
fireEvent.click(save);
})
expect(..........).(......);
});
import axios from 'axios'
--> import MockAdapter from 'axios-mock-adapter'
const mock = new MockAdapter(axios);
mock.onPost(/]/path\/some\/?/g).reply(config=>{
const {data} = config;
try{
return [200, successes: data, errors: []},{}];
}catch(err){
return [500, { error: 'sometin wrong' }, {}]
}
});
None of the above worked to get me coverage for the catch block that sets the error state which when set, would increase branch, line coverage. I need help to cover the catch block. Thanks in advance.

getStaticProps returns data not defined In nextjs

I want to fetch single data by id and I am using getStaticPaths and getStaticProps but I am getting the error data is not defined. Where am I going wrong Please help
My [id].tsx file
import MainComponentLayout from "../../components/Layout/MainLayoutComponent"
import EditProject from "../../components/EditProjectForm";
// HOW MANY HTML PAGES NEEDS TO BE MADE BASED ON OUR DATA
export const getStaticPaths = async () => {
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects`)
const data = await response.json()
console.log(data)
const path = data.result.map(project => {
return{
params: {id:project.project_ID}
}
})
return{
paths:path,
fallback: false
}
}
// FETCH SINGLE DATA
export const getStaticProps = async (context) => {
const id = context.params.id
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects/${id}`)
// Single Object
const data = await response.json()
return{
props: {fetchedData:data},
}
}
const EditForm = () => {
return(
<MainComponentLayout ComponentToRender = {<EditProject fetchedData = {fetchedData}/>}/>
)
}
export default EditForm
Change const EditForm = () => { to const EditForm = ({fetchedData}) => and it will work.
The getStaticProps, as its name implies, passes the fetched props object to the function as properties. You need to define them in the function as an object, and you can also destructure as in the example above, basically defining a fetchedData variable.
If You want to use props {fetchedData:data} in your app, You need pass them to the page component as props. As we can read in docs:
props - An optional object with the props that will be received by the
page component. It should be a serializable object
Here You have example page with getStaticProps() correctly used.
and Your code with props, Good Luck ! ;-)
import MainComponentLayout from "../../components/Layout/MainLayoutComponent";
import EditProject from "../../components/EditProjectForm";
const EditForm = ({ fetchedData }) => {
return (
<MainComponentLayout
ComponentToRender={<EditProject fetchedData={fetchedData} />}
/>
);
};
// FETCH SINGLE DATA
export const getStaticProps = async (context) => {
const id = context.params.id;
const response = await fetch(
`http://b560-45-248-23-129.ngrok.io/projects/${id}`
);
// Single Object
const data = await response.json();
return {
props: { fetchedData: data },
};
};
// HOW MANY HTML PAGES NEEDS TO BE MADE BASED ON OUR DATA
export const getStaticPaths = async () => {
const response = await fetch(`http://b560-45-248-23-129.ngrok.io/projects`);
const data = await response.json();
console.log(data);
const path = data.result.map((project) => {
return {
params: { id: project.project_ID },
};
});
return {
paths: path,
fallback: false,
};
};
export default EditForm;

How to wait until object is fully loaded when fetching with async/await in React

I use React for fetching voting objects of a GraphQL API, provided by AWS Amplify. Therefore I created following function that works with async/await:
import { useState, useEffect } from 'react';
import { API } from 'aws-amplify';
import { getVote } from 'src/graphql/queries';
const asGetVoting = (id) => {
const [vote, setVote] = useState([]);
const fetchVoting = async () => {
try {
const voteData = await API.graphql({
query: getVote, variables: { id }
});
setVote(voteData.data.getVote);
} catch (error) {
console.log('Fetching error: ', error);
}
};
useEffect(() => {
fetchVoting();
}, []);
return vote;
};
export default asGetVoting;
In my component I call the function above and I want to wait until the whole object is fetched - without success:
import asGetVoting from 'src/mixins/asGetVoting';
const Voting = () => {
const fetchVoting = asGetVoting(id);
fetchVoting.then((voting) => {
console.log('Voting completely loaded and ready to do other stuff');
}).catch((error) => {
console.log(error);
});
return (
<div>
some code
</div>
);
};
export default Voting;
Any idea what I am doing wrong? Respectively how can I wait until the object is loaded for querying its content? Or is my fetching function (asGetVoting) built in a wrong way? Do I mix async/await stuff with promises?
Thank you for your appreciated feedback in advance.
I think this is a little more complex than it needs to be. If API is returning a promise, you could set your state using .then to ensure the promise has resolved (I didn't included it but should probably add a catch statement as well). Something like:
const asGetVoting = (id) => {
const [vote, setVote] = useState([]);
useEffect(() => {
API.graphql({
query: getVote, variables: { id }
}).then(result => setVote(result.data.getVote))
}, []);
return (
// Whatever logic you are using to render vote state
<div>{vote}</div>
)
};

Resources