I have started the development of an application with React, Redux and Firebase. But I have a problem when I am using Firebase Storage.
When I use my code:
getImage (image) {
let { state } = this
st.doGetArticlesImg(image).then((url) => {
state[image] = url
this.setState(state)
}).catch((error) => {
console.log(error);
})
}
My images load correctly but the loading is very long, they appear only 3 or 4 seconds after loading the page. And once in two they do not charge.
Surely a life cycle story of a component?
You get the state:
let { state } = this
And then modify it:
state[image] = url
Don't forget - you can't mutate the state. It seems like, after mutating the state react doesn't see the difference and doesn't call render(). I'd re-wrote this like:
this.setState({image: url})
Related
In my current situation, I use Apollo useQuery to fetch a user and their posts.
The posts render in a summary view and in a detailed view. the detailed view is sitting on top of the summary view and its visibility is controlled by a piece of state.
const { data: userData, error: userError } = useQuery(GET_USER_BY_ID, { // the data fetch
variables: {
userId: getUserId(),
}
})
const [showPosts, setShowPosts] = useState(false) // controls toggle between views
the problem im having is every time the showPosts state changes the component re renders and the useQuery is run again. which I do not want. I have already got all my data, I just want to render html without hitting the server again until I need to. What is a potential solution to my problem?
Check skip param, You can do something like this:
const { data: userData, error: userError } = useQuery(GET_USER_BY_ID, { // the data fetch
variables: {
userId: getUserId(),
skip:!showPosts
}
})
or use useLazyQuery
I'm slowly understanding the services and fetch with React but when I try to show something, it shows me absolutely nothing. I put the code in case someone can help me. Maybe I have to look at how to work with JSON, I don't know.
let datosApi = [];
const recogerDatos = () => {
let json = "https://jsonplaceholder.typicode.com/albums";
let miApi = "http://localhost/dsw/api/";
fetch(json)
.then(data => data.json())
.then(info => {
console.log(info);
this.datosApi = info;
})
}
function Services() {
return (
<>
{datosApi.map(datos => (
<p>{datos.title}</p>
))}
</>
);
}
export default Services;
JSON data appears in https://jsonplaceholder.typicode.com/albums
I think your example is missing something or you've not done it.
Basically there's a few things wrong:
recogerDatos is never being called
datosApi is not declared, and even if it was, it's not stateful, thus won't cause a re-render of your items.
I've created a working sandbox here that shows it working, and the code is below:
const [result, setResult] = useState([]);
const recogerDatos = () => {
let json = "https://jsonplaceholder.typicode.com/albums";
fetch(json)
.then((data) => data.json())
.then((info) => {
console.log(info);
setResult(info);
});
};
useEffect(() => {
recogerDatos();
}, []);
return (
<div className="App">
{result.length > 0 && result.map((datos) => <p>{datos.title}</p>)}
</div>
);
The recogerDatos function is called on page load (see useEffect), and the result is updated when the fetch is successful. This causes a re-render and the items are shown.
You are displaying data from your list
let datosApi = [];
However it does not seem like you are populating your list with data from the API since the method recogerDatos() is never being called.
From your code it seems like you're missing some core React patterns like state management and the components lifecycle. Since React re-renders components a lot you want to store things like fetched data into state to avoid them constantly reset to their initial value. And when you want to fetch the data you usually don't want to do it at each re-render (that would cause A LOT of fetching), instead you usually want to trigger it based on different events, for example when component (that will be used to show this data) is mounted. Such things are usually using the components lifecycle methods / useEffect hooks to ensure that they happen at the right point in time. I recommend you to go into React documentation and study the basics a bit more so you can understand the main concepts when you're coding, but here's a quick sandbox with an example of how you could get the desired effect in React:
https://codesandbox.io/s/beautiful-frost-on9wmn?file=/src/App.js
I have a react query which writes the state variable- follower, and I want to access this variable in other component to find its .length can someone tell me how do I do it
const ModalFollower = ({profile}) => {
const [follower,setFollower] = useState([])
const {
data: followerName,
isLoading: followerLoading,
isFetching: followerFetching
} = useQuery(["invitations", profile?.id], () => {
getFollowers(profile?.id).then((response) => {
if (response) {
setFollower(response);
}
});
});
return(
{
!followerLoading && (
follower.map((e) => {
return(<>
<p>{e.requested_profile.Userlink}</p>
</>}
)
}
)
I want to access the length of follower in some other component
There is no need to copy data from react-query to local state, because react-query is a full-blown state manager for server state. As long as you use the same query key, you will get data from its cache. This is best abstracted away in custom hooks.
Please be aware that with the default values, you will get a "background refetch" if a new component mount, so you will see two network requests if you use it twice. That might look confusing at first, but it is intended, as it is not react-query's primary goal to reduce network requests, but to keep your data on the screen as up-to-date as possible. So when a new component mounts that uses a query, you'll get the stale data from the cache immediately, and then a background refetch will be done. This procedure is called stale-while-revalidate.
The best way to customize this behaviour is to set the staleTime property to tell react-query how long your resource is "valid". For that time, you will only get data from the cache if available. I've written about this topic in my blog here: React Query as a State Manager.
React Query also provides selectors, so if your second component is only interested in the length, this is what my code would look like:
const useInvitations = (profile, select) =>
useQuery(
["invitations", profile?.id],
() => getFollowers(profile?.id),
{
enabled: !!profile?.id
select
}
)
Note that I also added the enabled property because apparently, profile can be undefined and you likely wouldn't want to start fetching without that id.
Now we can call this in our main component:
const ModalFollower = ({profile}) => {
const { data } = useInvitations(profile)
}
and data will contain the result once the promise resolves.
In another component where we only want the length, we can do:
const { data } = useInvitations(profile, invitations => invitations.length)
and data will be of type number and you will only be subscribed to length changes. This works similar to redux selectors.
So I'm trying to understand React Hooks and how to use them. I can make a fetch inside a component as follows:
var [pages,setPages] = useState();
var [page,setPage] = useState();
async function fetchData() {
await fetch(`https://myBackend/pages`)
.then(response => response.json())
.then(response => {
setPages(response);
console.log(pages[0].title);
})
.then(()=>{
// AT THIS STAGE, pages IS UNDEFINED
setPage(pages.filter(singlepage=> {
return singlepage.url == props.match.params.url;
}))
}
.catch(err => setErrors(err));
}
useEffect(() => {
fetchData();
console.log(pages[0].title);
return () => {
console.log('unmounting...');
}
},[]);
I call to my backend to get all the pages, which works fine, as if I harcode pages[0].title it will render. But when I'm trying to access specific values in pages within the useEffect hook, for me to assign page, it gives me the error of pages being undefined. My console logging makes this apparent.
I want page as well as pages to be defined on 'component mounting', prior to the page loading. So my question is when does setPages actually set the page? and is it possible for me to assign both within useEffect, and based off each other?
Asynchronous actions take time. An indeterminate amount of time could theoretically pass. So, there's no way to guarantee that your data fetches before mounting your component.
Instead, you need to act as if the data could be undefined and have a state of the application that handles that.
As for getting access to the pages variable immediately after calling setPages, that will also fail because React actually runs the re-render asynchronously.
Even if it ran at the same time, there's a thing called closures in which javascript pulls in all variables around a function when it is created, so that the function always has access to those. React Hooks work by utilizing these closures to only have access to the variables as they were when the component was rendered/re-rendered. The reason this is the case, is because every re-render, all of the functions in your component are re-created which creates a new closure.
So, this means that you'll need to keep these concepts in mind as you work with React.
As for your code, the results solution that Dlucidione set up is one of your best bets, aside from setting up a separate useEffect to update page when pages changes.
const history = useHistory(); //from react-router-dom
useEffect(() => {
async function fetchData() {
return await fetch(`https://myBackend/pages`)
.then((response) => response.json())
.then((response) => {
setPages(response);
})
.catch((err) => setErrors(err));
}
fetchData();
return () => {
console.log('unmounting...');
};
}, []);
useEffect(() => {
const url = props.match.params.url;
if (!pages || !url) return;
// Using Array#find here because I assume based on the name, you want one page rather than an array
const page = pages.find((singlepage) => {
return singlepage.url === url;
});
if (!page) {
// This will change the route if a page isn't found.
history.push('/404');
}
setPage(page);
}, [pages, props.match.params.url, history]);
Redirect and history.push are different in how they work. Redirect is the declarative navigation method, while history.push is the programmatic.
Example usage of history.push:
if (!page) {
// This will change the route if a page isn't found.
history.push('/404');
}
Important note, this can be used anywhere in the code as long as you can pass the history object to there. I've used it within redux thunks before as well.
Example usages of Redirect without it redirecting on mount:
if(!pages && !page){
return <Redirect to="/404"/>
}
Or within some JSX:
<div>
{!pages && !page && <Redirect to="/404"/>}
</div>
Redirect has to be rendered which means its only usable within the return statement of a component.
The way I'm understanding it: the redirect is based on if the
individual page is found in the mounting process, therefore the
redirecting process has to also be in the mounting process to check
before rendering anything to redirect or not.
This is correct. When the Redirect itself mounts or updates, it will redirect if the conditions are correct. If there's a path or from prop on the Redirect (they are aliases of each other), then that limits the Redirect to only work when that path matches. If the path doesn't match, the redirect will do nothing until the path is changed to match (or it unmounts, of course).
Under the hood, Redirect just calls history.push or history.replace in componentDidMount and componentDidUpdate (via the Lifecycle component), so take that as you will.
useEffect(() => {
const fetchData = async () => {
try {
// Make a first request
const result = await axios.get(`firstUrl`);
setPages(result);
// here you can use result or pages to do other operation
setPage(result.filter(singlepage=> {
return singlepage.url == props.match.params.url;
or
setPage(pages.filter(singlepage=> {
return singlepage.url == props.match.params.url;
}))
} catch (e) {
// Handle error here
}
};
fetchData();
}, []);
When Promise.all resolves and the new activity is saved, the user should be routed to /activities to view their newly created activity. Everything works as expected, however I currently need to refresh /activities page (once) after being routed in order to view the new activity in the table.
const handleSaveActivity = e => {
e.preventDefault();
Promise.all([
addActivity(),
saveActivity()
]).then(() => {
props.history.push('/activities');
})
};
I'm not sure how to re-render the page automatically after pushing a new history state, so the user does not need to manually refresh the page to see the new state. Happy to provide more code snippets if I left out something critical.
Hi i must be a little late to answer this, but this issue can be due to the wrong use of useEffect, if you have lets say a todo list and you wanna fetch data with axios for example, it would look like this:
useEffect(()=>{
axios.get(`${YOUR_URL}/todos`)
.then((res)=>{
setTodos(todos=res.data)
})
},[])
now as you can see we have initial value of an empty array, so this is acting as a ComponentDidMount, what you might want is to re render the component after it gets a new value, so you want to have a ComponentDidUpdate effect, so you would just not initialize the value as an empty array, therefore it would look like this:
useEffect(()=>{
axios.get(`${YOUR_URL}/todos`)
.then((res)=>{
setTodos(todos=res.data)
})
})
Hope this helps someone, couse i landed here due to the same issue and came to solve it this way.
just to run this.setState({whateverKey:whateverValue})?
In your activities page (call it Activities component) you should call API to get the updated data every time browser hit this component URL.
With class based style, you should do it in componentDidMount life cycle hook
class Activities extends Component {
// ...
componentDidMount() { loadActivities() }
// ...
}
With function based style, you should do it in useEffect hook
import React, { useEffect } from 'react'
const Activities = () => {
useEffect(() => { loadActivities() });
}
https://github.com/supasate/connected-react-router Please use this package, it solves the problem.
This issue I've faced a few minutes ago...however I finally found the solution by manually using the vanilla javascript. => for refreshing the page you can use
=> window.location.reload(false); after using the push property.