component is getting loaded for fraction of second in React - javascript

I have one query call which returns me the data and also I am using await as well.
const {data, isLoading } = useQuery('getData', queryFunc);
here ,
I am trying to use it here
<div>
{ data?.addition?.isEnabled && <Notification> }
</div>
Here, for fraction of second the Notification component is getting rendered., and after 1-2 second once response comes then the component is rendered.
Is there any specific reason for this? also How do I fix this ?
I fixed this like { data && data?.addition?.isEnabled && <Notification> }
can any one help me with this?

In React undefined and false values are not rendered. Since the data you're getting is from an async function the notifications component will be rendered briefly until initialized and then removed. You can maybe use a useCallback hook to fix this behavior but without seeing more of the code I'm not sure on what to do.
Usage of useCallback is like so:
const foo = useCallback(() => { //you can pass parameters here when calling like this foo(arg1,arg2)
//do something expensive in resources...
//this will re-render when done.
return bar;
},[//dependencies go here]);
//use with useEffect hook if you want this to render on load like so:
useEffect(() => {
foo();
},[foo]);

Related

need to pass an effect via props or force component reload from outside the root component in preact/react

I have a situation that an item outside the component might influence the item in the backend. ex: change the value of one of the properties that are persisted, let's say the item status moves from Pending to Completed.
I know when it happens but since it is outside of a component I need to tell to the component that it is out of sync and re-fetch the data. But from outside. I know you can pass props calling the render method again. But the problem is I have a reducer and the state will pick up the last state and if I use an prop to trigger an effect I get into a loop.
Here is what I did:
useEffect(() => {
if (props.effect && !state.effect) { //this runs when the prop changes
return dispatch({ type: props.effect, });
}
if (state.effect) { // but then I get here and then back up and so on
return ModelEffect[state.effect](state?.data?.item)}, [state.status, state.effect, props.effect,]);
In short since I can't get rid of the prop the I get the first one then the second and so on in an infinite loop.
I render the root component passing the id:
render(html`<${Panel} id=${id}/>`,
document.getElementById('Panel'));
Then the idea was that I could do this to sync it:
render(html`<${Panel} id=${id} effect="RELOAD"/>`,
document.getElementById('Panel'));
any better ways to solve this?
Thanks a lot
I resolved it by passing the initialized dispatch function to a global.
function Panel (props) {
//...
const [state, dispatch,] = useReducer(RequestReducer, initialData);
//...
useEffect(() => {
//...
window.GlobalDispatch = dispatch;
//...
}, [state.status, state.effect,]);
with that I can do:
window.GlobalDispatch({type:'RELOAD'});

How do I obtain updated props mid-async-function?

This is for an open-source project called react-share, and their ShareButton component has a prop called beforeOnClick that you can pass to it. I'm using beforeOnClick to upload an image to our CDN so that we don't needlessly upload images that don't get shared, which causes the url prop passed to the button to update.
My current problem is, after beforeOnClick runs, the share button currently doesn't handle the updated url prop.
Basically, I have an async function that looks something like this:
const handleClick = async () => {
const { url, disabled, beforeOnClick } = this.props;
// beforeOnClick can cause this.props to change. beforeOnClick can also perform async operations, like making a fetch call
if (beforeOnClick) {
await beforeOnClick();
// call setTimeout to delay the next handleClick call in order to ensure this.props
// properly reflects changes from the parent component
setTimeout(handleClick);
return;
}
// Do stuff with url & disabled
};
I dumbed it down for the sake of keeping the question simple, but if you'd like to view the code I currently have, check out my fork. compare to the original.
Is setTimeout a reliable way to achieve this effect? Or, should I do something like this instead:
this.setState({ __rerender_component: true }, handleClick);
I'm not a huge fan of that solution, as I'd have to manage resetting that flag after the callback is run. Any thoughts are appreciated!
EDIT: Using setTimeout seems to work, but I'm not sure if it's reliable. If it fails 1/100 times, that sucks.
It might be easiest and feels more "reacty" to use setState to have a local copy of the props and let the beforeOnClick function use setState?
eg (beware, I have been using hooks only on my latest projects, so might be off)
const handleClick = async () => {
this.state = this.props; // can all props be changed?
if (beforeOnClick) {
await beforeOnClick(this.setState);
// Do stuff with this.state.url & this.state.disabled };
and beforeOnClick can use setState to change the url and others.
instead of giving full control to setState, you might want to have a different approach:
let newState= await beforeOnClick();
if (newState && newState.url && !newState.url.startsWith("http"))
throw 'url must start with http';
// that might be a wrong assumption, take it as an example
// whatever else you want to check, like disable is a boolean...
this.setState({...state, ...newState});

How to reduce the number of times useEffect is called?

Google's lighthouse tool gave my app an appalling performance score so I've been doing some investigating. I have a component called Home
inside Home I have useEffect (only one) that looks like this
useEffect(() => {
console.log('rendering in here?') // called 14 times...what?!
console.log(user.data, 'uvv') // called 13 times...again, What the heck?
}, [user.data])
I know that you put the second argument of , [] to make sure useEffect is only called once the data changes but this is the main part I don't get. when I console log user.data the first 4 console logs are empty arrays. the next 9 are arrays of length 9. so in my head, it should only have called it twice? once for [] and once for [].length(9) so what on earth is going on?
I seriously need to reduce it as it must be killing my performance. let me know if there's anything else I can do to dramatically reduce these calls
this is how I get user.data
const Home = ({ ui, user }) => { // I pass it in here as a prop
const mapState = ({ user }) => ({
user,
})
and then my component is connected so I just pass it in here
To overcome this scenario, React Hooks also provides functionality called useMemo.
You can use useMemo instead useEffect because useMemo cache the instance it renders and whenever it hit for render, it first check into cache to whether any related instance has been available for given deps.. If so, then rather than run entire function it will simply return it from cache.
This is not an answer but there is too much code to fit in a comment. First you can log all actions that change user.data by replacing original root reducer temporarlily:
let lastData = {};
const logRootReducer = (state, action) => {
const newState = rootReducer(state, action);
if (newState.user.data !== lastData) {
console.log(
'action changed data:',
action,
newState.user.data,
lastData
);
lastData = newState.user.data;
}
return newState;
};
Another thing causing user.data to keep changing is when you do something like this in the reducer:
if (action.type === SOME_TYPE) {
return {
...state,
user: {
...state.user,
//here data is set to a new array every time
data: [],
},
};
}
Instead you can do something like this:
const EMPTY_DATA = [];
//... other code
data: EMPTY_DATA,
Your selector is getting user out of state and creating a new object that would cause the component to re render but the dependency of the effect is user.data so the effect will only run if data actually changed.
Redux devtools also show differences in the wrong way, if you mutate something in state the devtools will show them as changes but React won't see them as changes. When you assign a new object to something data:[] then redux won't show them as changes but React will see it as a change.

Fetch data and setState every time just before component renders in React Native

There is the case , i have a modal which shows some data from its state ( an array ) , and it's state is getting set in componentDidMount() function like docs suggests. I need to show updated data every time when modal opened up.So i was able to do that with componentWillReceiveProps(nextProps) function , like i showed below .
But what if i want to migrate to getDerivedStateFromProps function ? How am i going to achieve same behaviour with it ?
Here is the component code simplified for sake :
export class PastOrdersModal extends Component {
constructor(props) {
super(props);
this.state = {
past: {},
isLoading: false,
modalVisible: false
};
}
async componentWillReceiveProps(nextProps) {
const response = await this.fetchPast();
this.setState({ past: response });
}
async componentDidMount() {
const response = await this.fetchPast();
this.setState({ past: response });
}
...
... some callback functions for handling modalvisible value ....
...
render(){
// here rendering state array with this.state.past.map(......) etc.
}
The fetchPast function makes a GET request to server of mine , all you need to know that it returns an array to me. This is working perfectly. ComponentWillReceiveProps gets called every time because parent component sends modalVisible props everytime.
But componentWillRecieveProps is deprecating and i could not make the same behavior with getDerivedStateFromProps.How should i implement same thing with it.
Note: ı am not going to use redux ,i am not going to use mobx , i know how to use them and it's not what i want. The thing is the behavior i want is soo simple i don't want to pass values another component , i don't want to pass values another screen , i just want to update a simple component that is all but either the framework is pushing it's limits to make simplest tasks hardests thing ever or i am missing really huge point.(probably latter one :) )
Note: I know i am not doing anything with nextProps , that was the only solution i found.
You can write your Component as a function so you can use React Hooks
const PastOrdersModal = (props) => {
const [past, setPast] = useState({});
const [isLoading, setLoading] = useState(false);
const [modalVisible, setModalVisibility] = useState(false);
useEffect(() => {
const fetchPast = async () => {
const response = await fetchPast();
setPast(response);
};
if(modalVisible) fetchPast();
}, [modalVisible])
useEffect(() => {
const fetchPast = async () => {
const response = await fetchPast();
setPast(response);
};
fetchPast();
}, [])
...
... some callback functions for handling modalvisible value ....
...
return <YourComponent />
The variables we created inside [] at the top are the ones we are using for the state. The first one will be the state as itself and the second one is a function responsible for updating that state. The useEffect hook will simulate the life cycle methods for Class Components, it receives a callback that will be executed and a second argument. The second argument is the one that will indicate when it is going to be triggered. For instance, you can see 2 useEffects, the one with the empty array will tell the hook to execute just once, similar as componentDidMount. The other one will be triggered when modalVisible changes, so everytime you change its value it will be executed, that's why we only validate if the modal is visible (is true) we do the fetch, otherwise the fetch won't be executed

Why is my component that receives props not working when I destructure out the properties, but when I use props.key it's working?

The Problem
I have an application that uses this React Redux Boilerplate: https://github.com/flexdinesh/react-redux-boilerplate
I created a new page that is connected to the injected reducer + saga.
I receive following props: posts, loading, error, loadPosts and match
When I use these directly the app is working as expected. But as soon as I start to destructure the props, the app is behaving unexpectedly.
Especially with the match props.
When I do it like this:
const SubforumPage = (props) => {
useEffect(() => {
const { id: subId } = props.match.params;
console.log('props: ', subId);
}, []);
// .... other code
}
No problem everything works.
But when I do it like this:
const SubforumPage = ({match}) => {
useEffect(() => {
const { id: subId } = match.params;
console.log('props: ', subId);
}, []);
// .... other code
}
match suddenly gets undefined!
I have really no clue what so ever why this is happening. It's the first time that I see an error like this.
This specific page is set up like this in the routing file:
<Route path="/sub/:id" component={SubforumPage} />
And it's clearly working when using (props) in the function arguments but not with ({match})
Why is this? Can please someone help me out here.
What have I tried?
I continuesly started destructuring one prop after another. At first this approach works and it's still not undefined but when I get to some props, it's different which ones, it will stop working.
I think it has to do something with how I use my useEffect() hook?
I pass an empty array so it does just run when mounting. It seems like when I refresh the page, the posts are cleared out but the useEffect doesn't run anymore, so the new posts doesn't get fetched. Because hen also the console.log inside the useEffect hook is undefined doesn't even run. But for example the loading prop in console.log outside of useEffect is indeed not undefined
(But that still does not explain why it's working with (props) as argument).
Am I just using useEffect wrong?
Many thanks
Ok guys that was completely my fault. Guess I'm too tired :D. Here is what caused the problem:
I fetch my post in the useEffect hook. I also render a component where I pass in the posts. But the posts are not available because the component has to wait for the data to come in. So I completely forgot that I have to wait for the data.
Before:
return <PostsGroup posts={posts} />;
After: (correct)
return <PostsGroup posts={posts || []} />;
I had a check in place looking like this:
if (loading) return <CircularProgress />;
(before the other return). But it doesn't matter because loading is false when the component initially renders.
So I also set the initial value from loading to true (in my initialState of the reducer). So I have now two checks in place.
Sorry guys. So stupid.

Categories