Hello I am trying to render the updated stuff from the database whenever the update is invoked to the database. I found this solution but right now my "add" method is not in the same file as the "fetch" method like in the linked question. I tried the below but it still isn't working:
file 1: (the return method will render the UploadedImages by mapping them)
const [UploadedImages,setUploadedImages] = useState([])
const fetchUserAllPhotos = async () => {
const res = await client.get('/get-photos')
setUploadedImages(res.data.photoList)
}
useEffect(()=>{
fetchUserAllPhotos()
},[])
file 2:
import {fetchUserAllPhotos} from './Gallery/AllPhotos';
const addNewPhoto = async () => {
if (success) {
await fetchUserAllPhotos()
}
}
However inside the file 1's return (render) method it is not giving the updated result whenever a new photos is added (I need to sign out and sign back in in order to see the change). How can I go about solving it?
what if the function is wrapped inside another one?
export default function PhotoGrid(props){
const [UploadedImages,setUploadedImages] = useState([])
const fetchUserAllPhotos = async () => {
const res = await client.get('/get-photos')
setUploadedImages(res.data.photoList)
}
useEffect(()=>{
fetchUserAllPhotos()
},[])
}
you need to add this line at the bottom of file1
export {fetchUserAllPhotos}
In general, every time you want to import function, array, object etc it should look like this:
file1:
const func = () => {
//implementaion of the function
}
export {func}
file2:
import {func} from './file1' //you need to give the path of file1
func() //now you can use the fanction everywhere in file2
note: you can export as many functions as you wants just be careful it important to use the same name in the export and in the import
export {func1, func2, func3, obj1, abj2, arr1, arr2}
if you are exporting function from react component you need to define the function outside the component
const func = () => {
}
export default function MyReactComponent(prop) {
//implementation of your component
}
export {func}
If you need to use prop of your component inside the function you can pass this prop as a function parameter.
I recomend to avoid from exporting function from react component because when your project becomes bigger it will start to be frustrate to find were you impliment each fanction. instead it is a best practice to add a new js file that you implement there all the fetches function, I usually call this file api.js
This file should look something like this (I took this code from my project there I used axios to make the fetches to the server):
import axios from "axios"
const url = 'http://localhost:8080'
const api = {
getQuestions : async () => {
return axios.get(`${url}/question/all`)
},
getQuestionById : async (id) => {
return axios.get(`${url}/question/${id}`)
}
}
export default api;
Related
How can I create a component for Axios that I can use in different places with different values ??
I got stuck, what should I do?
This is what I have achieved so far
thank you for Helping
import axios from "axios";
const Axios = (props) => {
const [posttitle, postbody] = useState([]);
const [postuserid, postid] = useState([]);
const fetchData = () => {
const { postbodyapi } = props.postbodyapi;
const postuseridapi = "https://nba-players.herokuapp.com/players/james/lebron";
const getbody = axios.get(postbodyapi);
const getuseid = axios.get(postuseridapi);
axios.all([getbody, getuseid]).then(axios.spread((...allData) => {
const databody = allData[0].data.first_name;
const datauseid = allData[1].config.url;
postbody(databody);
postid(datauseid);
}))
}
useEffect(() => {
fetchData()
}, [])
return (
<div className="App">
{posttitle}
<img src={postuserid} alt="asd"/>
</div>
);
}
export default Axios;
You should create a custom hook.
Create a hook called for example useAxios and hold only the fetching method inside of it, and the return state from that hook should be just data.
you can make it so it takes params like "URL, data, method", or make a few smaller hooks like useAxiosGet, useAxiosPost.
If you make a few smaller it will be easier to read and change something if needed.
Here is how I did it, an example of one specific Axios custom hook, use this for example to see how to build it.
useGetCar.js // custom axsios hook
import axios from 'axios';
const useGetCar = async (url, id) => {
const result = await axios.post(url, {id: id});
return result.data[0];
}
export default useGetCar
car.js // page component that displays data
import useGetCar from "#hooks/useGetCar";
let car_id = 1; // some that i send to api
// this function here is not exact from my code,
//but I just wanted to provide you an example.
// I didn't include my original code because it is
//from next.js app and I don't want to confuse u with that
async function getData() {
let car = await useGetCar(`http://localhost/get_car.php`, car_id);
return car;
}
Hope you understood what I'm saying, and I did not confuse you.
Feel free to ask anything if you don't understand something clearly.
Happy coding.
I am following along in a React course on Udemy. In this module, we have a simple task app to demonstrate custom hooks. I've come across a situation where the "task" state is being managed in the App.js file, the "useHttp" custom hook has a function "fetchTasks" which accepts "transformTasks" as a parameter when called inside App.js. The issue I am having is that "tranformTasks" manipulates the "tasks" state inside App.js, but it is actually being called and executed inside the "useHttp" custom hook. Would really love some help understanding the mechanism for how this works. How can the state be manipulated while called from another file without the state being passed in? The code does work as intended. Here's the github link to the full app, and below are the two relevant files: https://github.com/yanichik/react-course/tree/main/full-course/custom-hooks-v2
Here is the App.js file:
import React, { useEffect, useMemo, useState } from "react";
import Tasks from "./components/Tasks/Tasks";
import NewTask from "./components/NewTask/NewTask";
import useHttp from "./custom-hooks/useHttp";
function App() {
// manage tasks state here at top level
const [tasks, setTasks] = useState([]);
const myUrl = useMemo(() => {
return {
url: "https://react-http-104c4-default-rtdb.firebaseio.com/tasks.json",
};
}, []);
const { isLoading, error, sendRequest: fetchTasks } = useHttp();
useEffect(() => {
// func transforms loaded data to add id (firebase-generated), push to loadedTasks, then
// push to tasks state
const transformTasks = (taskObj) => {
let loadedTasks = [];
for (const taskKey in taskObj) {
loadedTasks.push({ id: taskKey, text: taskObj[taskKey].text });
}
setTasks(loadedTasks);
};
fetchTasks(myUrl, transformTasks);
// if you add fetchTasks as a dependency this will trigger a re-render each time states
// are set inside sendRequest (ie fetchTasks) and with each render the custom hook (useHttp)
// will be recalled to continue the cycle. to avoid this, wrap sendRequest with useCallback
}, [fetchTasks, myUrl]);
const addTaskHandler = (task) => {
setTasks((prevTasks) => prevTasks.concat(task));
};
return (
<React.Fragment>
<NewTask onEnterTask={addTaskHandler} />
<Tasks
items={tasks}
loading={isLoading}
error={error}
onFetch={fetchTasks}
/>
</React.Fragment>
);
}
export default App;
And here is the "useHttp" custom hook:
import { useState, useCallback } from "react";
// NOTE that useCallback CANNOT be used on the top level function
function useHttp() {
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const sendRequest = useCallback(async (httpConfig, applyFunction) => {
setIsLoading(true);
setError(false);
try {
const response = await fetch(httpConfig.url, {
method: httpConfig.method ? httpConfig.method : "GET",
headers: httpConfig.headers ? httpConfig.headers : {},
body: httpConfig.body ? JSON.stringify(httpConfig.body) : null,
});
// console.log("response: " + response.method);
if (!response.ok) {
throw new Error("Request failed!");
}
const data = await response.json();
applyFunction(data);
// console.log("the formatted task is:" + applyFunction(data));
} catch (err) {
setError(err.message || "Something went wrong!");
}
setIsLoading(false);
}, []);
return { sendRequest, isLoading, error };
}
export default useHttp;
Sounds like you're learning from a decent course. The hook is using a technique called "composition". It knows you'll want to do some processing on the data once it has been fetched and let's you pass in (the applyFunction variable) your own snippet of code to do that processing.
Your snippet of code is just a function, but all parties agree on what parameters the function takes. (This is where using typescript helps catch errors.)
So you pass in a function that you write, and your function takes 1 parameter, which you expect will be the data that's downloaded.
The useHttp hook remembers your function and once it has downloaded the data, it calls your function passing in the data.
If you've used some of your own variables within the function you pass to the hook, they get frozen in time ... sort-of. This can of worms is a topic called 'closures' and I'm sure it will come up in the course if it hasn't already.
I have a straightforward react component that looks so in AllWords.js :
import React, { useEffect, useState } from 'react';
import consts from '../../constants/Constants.js';
function AllWords() {
const [words, setWords] = useState([]);
async function fetchData(){
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
setWords(data);
};
// API: useEffect( () => { . . . return cleanup; },[var_n_whose_change_triggers_useEffect . . .] );
useEffect(() => {fetchData()}, [] );
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
I would like to refactor the fetchData() method out of the component into another file (basically a separate .js file that holds the fetch call).
What I would like is to have created a file titled FetchAllWords.js under src/actions/ & then import it. & use that.
I have several questions :
do I need to set the state in the FetchAllWords.js and then useSelector to extract the state in AllWords.js?
in FetchAllWords.js do I need to usedispatch to dispatch a method call setting the state? I would like to just setState in FetchAllWords.js and then extract it in AllWords.js. This is what I have so far:
import consts from '../constants/Constants.js';
import { useState } from 'react';
async function FetchAllWords(){
const [words, setWords] = useState([]);
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
setWords(data);
}
export default FetchAllWords;
I am unsure how to import this and use it in AllWords.js. I am using the following statement :
import wordList from '../../actions/FetchAllWords';
Then I am trying to use wordList as a handle to the file '../../actions/FetchAllWords.js' & attempting to access the async function FetchAllWords so wordList.FetchAllWords();
Firstly , the editor (VSCode) won't let me see the function despite the import call.
Secondly I am getting an error (something like) :
TypeError: _actions_FetchAllWords_js__WEBPACK_IMPORTED_MODULE_3__.default.FetchAllWords is not a function
Any insight or help would be appreciated since rather uneasy with JS & React.
The github repo is : https://github.com/mrarthurwhite/hooks-p5-react-redux
EDIT: As per David's suggestions :
So AllWords.js React component is :
import React, { useEffect, useState } from 'react';
import wordList from '../../services/Fetch.js';
function AllWords() {
const [words, setWords] = useState([]);
function fetchData(){
wordList.fetchAllWords().then(
data => setWords(data)
);
};
// API: useEffect( () => { . . . return cleanup; },[var_n_whose_change_triggers_useEffect . . .] );
useEffect(() => {fetchData()}, [] );
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
And Fetch.js is :
import consts from '../constants/Constants.js';
class Fetch {
async fetchAllWords(){
const response= await fetch(consts.FETCH_URL);
const data = await (response.json());
return data;
}
}
export default Fetch;
No, don't worry about state in the external file. Just focus on the one thing it should do, perform the AJAX operation. At its simplest it's just a function, something like:
import consts from '../../constants/Constants.js';
const fetchAllWords = async () => {
const response = await fetch(consts.FETCH_URL);
const data = await (response.json());
return data;
}
export default fetchAllWords;
You can even make it a class which contains this function, if you plan on adding other service operations as well. (Fetch specific word? Find word? etc.) The point is that this does just one thing, provide data. Let the React components handle React state.
Within the component you'd just use that to get your data. Something like:
import React, { useEffect, useState } from 'react';
import fetchAllWords from '../../services/FetchAllWords.js';
function AllWords() {
const [words, setWords] = useState([]);
useEffect(() => {
fetchAllWords().then(w => setWords(w));
}, []);
return (
<>
{
words.map(w=> <div>{w.word}</div>)
}
</>
);
}
export default AllWords;
Overall it's a matter of separating concerns. The service performs the AJAX operation and returns the meaningful data, internally concerned with things like JSON deserialization and whatnot. The React component maintains the state and renders the output, internally concerned with updating state after useEffect runs and whatnot.
Svelte store documentation shows String or Integer being updated, but I did not find any dynamic function in store.
I don't understand how to make the getData function as a writable in order to notify the html of the change.
In the following sample, I would like b to be shown after the updateKey function is called.
You will find a minimal code in REPL here: https://svelte.dev/repl/3c86bd48d5b5428daee514765c926e58?version=3.29.7
And the same code here in case REPL would be down:
App.svelte:
<script>
import { getData } from './store.js';
import { updateKey } from './store.js';
setTimeout(updateKey, 1000);
</script>
<h1>{getData()}!</h1>
store.js
import {setContext} from 'svelte';
import {writable} from 'svelte/store';
var data = {
'a': 'a',
'b': 'b'
};
var key = 'a';
export const getData = function() {
return data[key];
}
export const updateKey = () => {
key = 'b';
}
The goal is to work with a dynamic function in the store.
Well, I think you still have a bit of confusion about how things work in Svelte... Not sure how to best answer your question, so here's some code for what's you're trying to achieve, along with some comments. I hope it will help you better understand how things come together in regards to stores.
App.svelte
<script>
import { onMount } from 'svelte'
import { key, data, updateKey } from './store.js'
onMount(() => {
// it's not safe to have an unchecked timer running -- problems would
// occur if the component is destroyed before the timeout has ellapsed,
// that's why we're using the `onMount` lifecycle function and its
// cleanup function here
const timeout = setTimeout(updateKey, 1000);
// this cleanup function is called when the component is destroyed
return () => {
clearTimeout(timeout)
}
})
// this will log the value of the `key` store each time it changes, using
// a reactive expression (a Sveltism)
$: console.log($key)
</script>
<!--
NOTE: we're using the $ prefix notation to access _the value_ of the store,
and not `data`, which would be _the store itself_ (an object with
subscribe, set, etc.)
-->
<h1>{$data}</h1>
store.js
import { writable, derived } from 'svelte/store'
const db = {
'a': 'a',
'b': 'b'
}
// a writable store with initial value 'a'
export const key = writable('a')
export const updateKey = () => {
// a writable store has a `set` method to change its value
key.set('b')
}
// you can use a derived store to compute derived values from
// the current value of other stores
//
// here, we're getting the value from the db when the value of
// the `key` store changes
export const data = derived([key], ([$key]) => db[$key])
if I understood your question correctly, you want to be able to change the function (logic) that is executed by getData() and you want on each function change the html to be updated
for this use case you'll need to create your own custom store
as follows in store.js
import { writable } from 'svelte/store';
// an object to hold our functions
const functions = {
"funcA": () => {
// do something
return "whatevedata for a"
},
"funcB": () => {
// do something
return "the data of b"
}
}
// this how to create a custom store, taken from svelte documentation
function createCustomStore(defaultValue) {
const { subscribe, set, update } = writable(defaultValue);
return {
subscribe,
//custom function change func where suppliedValue the value you input to the store
// set() is a default function for a store to change it's value
changeFunc: (suppliedValue) => set(functions[suppliedValue]),
reset: () => set(defaultValue)
};
}
export const getData = createCustomStore(() => "default");
export const updateKey = () => {
// this to update which function the store uses
getData.changeFunc("funcB")
}
in App.svelte
<script>
import { getData } from './store.js';
import { updateKey } from './store.js';
setTimeout(function() {
updateKey()
}, 1000);
</script>
<h1>{$getData()}</h1>
we added the $ to getData because it's a store that holds reference to functions and the () is there to execute any function referenced by getData store. since it is a store on each value change (function change) of getData, the html will be updated
here is a repl of the implementation
After a huge amount of trial and error for a complex webGL project I have landed on a solution that will reduce the amount of re-engineering working, threejs code (from another developer) and, as this project is extremely time restrained, reduce the amount of time needed. It's also worth noting my experience of this is limited and I am the only developer left on the team.
The project current accepts a large array of random user data, which is exported from a js file and then consumed here...
import Users from "./data/data-users";
class UsersManager {
constructor() {
this.mapUserCountries = {};
}
init() {
Users.forEach(user => {
const c = user.country;
if (!this.mapUserCountries[c])
this.mapUserCountries[c] = { nbUsers: 0, users: [] };
this.mapUserCountries[c].nbUsers++;
this.mapUserCountries[c].users.push(user);
});
}
getUsersPerCountry(country) {
return this.mapUserCountries[country];
}
}
export default new UsersManager();
Here is my fetch request..
import { useState, useEffect } from "react";
const FetchUsers = () => {
const [hasError, setErrors] = useState(false);
const [users, setUsers] = useState({});
async function fetchData() {
const res = await fetch(
"https://swapi.co/api/planets/4/"
);
res
.json()
.then(res => setUsers(res))
.catch(err => setErrors(err));
}
useEffect(() => {
fetchData();
}, []);
return JSON.stringify(users);
};
export default FetchUsers;
I have run into lots of issues as the UserManager is a class component and if I import my fetchUsers into this file, call it and save it to a variable like so const Users = fetchUsers(); it violates hooks.
I want to be able to return a function that will return my users from the database as an array.
That will then be able to be passed into the UserManager in the same way the hard coded data is and mapped over to be actioned by LOTS of other files.
I've mocked up a small codesandbox with what the flow would be ideally but I know I need a solution outside of hooks...
https://codesandbox.io/s/funny-borg-u2yl6
thanks
--- EDIT ---
import usersP from "./data/data-users";
class UsersManager {
constructor() {
this.mapUserCountries = {};
this.state = {
users: undefined
};
}
init() {
usersP.then(users => {
this.setState({ users });
});
console.log(usersP);
this.state.users.forEach(user => {
const c = user.country;
if (!this.mapUserCountries[c])
this.mapUserCountries[c] = { nbUsers: 0, users: [] };
this.mapUserCountries[c].nbUsers++;
this.mapUserCountries[c].users.push(user);
});
}
getUsersPerCountry(country) {
return this.mapUserCountries[country];
}
}
export default new UsersManager();
console.log (UsersManager.js:16 Uncaught TypeError: Cannot read property 'forEach' of undefined
at UsersManager.init (UsersManager.js:16)
at Loader.SceneApp.onLoadingComplete [as callback] (App.js:39)
at Loader.onAssetLoaded (index.js:20)
at index.js:36
at three.module.js:36226
at HTMLImageElement.onImageLoad)
I fixed your sandbox example.
You cannot load the users synchronously (using import) as you need to make a http call to fetch the users so it's asynchronous.
As a result you can fetch the users inside the componentDidMount lifecycle method and use a state variable to store them once they are fetched
There are a couple guidelines that will help separate functions that are Hooks and functions that are Components (these are true most of the time):
1 Component functions use pascal case (start with a capital letter) and always return JSX.
2 Custom Hooks functions conventionally begin with the word "use" and never return JSX.
In your case you probably want to make a custom Hooks function that must be called in a component;
function useUserData() {
const [hasError, setErrors] = useState(false);
const [users, setUsers] = useState({});
const networkCall = useCallback(async fetchData = () => {
const res = await fetch(
"https://swapi.co/api/planets/4/"
);
res
.json()
.then(res => setUsers(res))
.catch(err => setErrors(err));
} , [])
useEffect(() => {
fetchData();
}, []);
return {users, hasError};
}
Then call that custom hook in one of your components:
function App() {
const {users, hasError} = useUserData();
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div>{users}</div>
<h2>Start editing to see some magic happen!</h2>
</div>
);
}
}
If you then need to share that fetched data throughout your app, you can pass it down via props or the context API: https://reactjs.org/docs/context.html
(post a message if you'd like an example of this).