How to wait promise solved afterthen render in reactjs - javascript

I got this error when using promise:
Warning: Can't perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function.
I'm pretty sure this error is got from my promise async request, when i not using promise it working fine.
But when im using promise to handle async request, i got this error, i need promise to handle it, so how can i fix this?
This is my Async Request.
export const LoginAPI = (username, password) => (dispatch) => {
const config = {
headers: {
'Content-Type': 'application/json',
},
};
const body = JSON.stringify({ username, password });
const promise = new Promise((resolve, reject) => {
axios.post('http://localhost:8000/api/accounts/login/', body, config)
.then((response) => {
dispatch({ type: LOGIN_SUCCESS, payload: response.data });
SwalAlert('Successfully Login...', 'Welcome To 7Blankpages...', 'success');
resolve(true);
})
.catch((err) => {
dispatch({ type: LOGIN_FAILED });
if (err.response.data.non_field_errors) SwalAlert('Oops...', `${err.response.data.non_field_errors}`, 'error');
reject(false);
});
});
return promise;
};
This is my Login Handling
const handleSubmit = async (e) => {
e.preventDefault();
setIsLoading(true);
const response = await LoginUser(username, password).catch(() => setIsLoading(false));
if (response) {
setIsLoading(false);
setUsername('');
setPassword('');
}
};
if (auth.isAuthenticated && auth.user.is_active) {
return <Redirect to="/" />;
}

This happened because your handlesubmit function is asynchronous that makes if block to get executed redirecting to url '/' changing the component. but the function setisloading,setusername etc in your handlesubmit will still be excecuting which led to memory leak. To prevent this add useEffect hook with cleanup function.
useEffect(()=>{
//cleanup function
return ()=>{
setisloading(false);
setusername('');
setpassword('');
}
})

Related

Cleaning up a useEffect warning with useReducer,

I keep getting these warnings:
Can't perform a React state update on an unmounted component.
This is a no-op, but it indicates a memory leak in your application.
To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup
For some of my useEffects that pull data from an API with the help of my useReducer:
export default function HomeBucketsExample(props) {
const {mobileView} = props
const [allDemoBuckets, dispatchAllBuckets] = useReducer(reducer, initialStateAllBuckets)
const ListLoading = LoadingComponent(HomeBucketLists);
useEffect(() =>
{
getAllDemoBuckets(dispatchAllBuckets);
}, [])
return (
<ListLoading mobileView={ mobileView} isLoading={allDemoBuckets.loading} buckets={allDemoBuckets.data} />
);
}
However, Im not sure how to clean up this effect above, I've tried mounting it using True and False, however the error still showed up. How can I fix my function above so the useEffect doesnt throw any warnings
EDIT:
code for my reduer:
export const getAllDemoBuckets = (dispatch) => axiosInstance
.get('demo/all/')
.then(response => {
dispatch({ type: 'FETCH_SUCCESS', payload: response.data })
console.log('fired bucket-data')
})
.catch(error => {
dispatch({ type: 'FETCH_ERROR' })
})
const initialStateAllBuckets = {
loading: true,
error: '',
data: []
}
const reducer = (state, action) =>
{
switch (action.type)
{
case 'FETCH_SUCCESS':
return {
loading: false,
data: action.payload,
error: ''
}
case 'FETCH_ERROR':
return {
loading: false,
data: {},
error: "Something went wrong!"
}
default:
return state
}
}
const [allDemoBuckets, dispatchAllBuckets] = useReducer(reducer, initialStateAllBuckets)
The goal of the warning is to tell you that some action is taking place after the component is unmounted and that the result of that work is going to be thrown away.
The solution isn't to try and work around it with a reducer; the solution is to cancel whatever is happening by returning a callback from useEffect. For example:
useEffect(() => {
const ctrl = new AbortController();
fetchExternalResource(ctrl.signal);
return () => {
ctrl.abort();
}
}, []);
Using flags to determine if a component is mounted (ie using a reducer) to determine whether or not to update state is missing the point of the warning.
It's also okay to leave the warning up if this isn't actually an issue. It's just there to nit pick and tell you that, hey, you may want to clean this up. But it's not an error.
In your case, if you are using fetch, I would modify your code such that the function that dispatches actions can take an AbortSignal to cancel its operations. If you're not using fetch, there's not much you can do, and you should just ignore this warning. It's not a big deal.
It looks like you're using Axios for your requests. Axios supports a mechanism similar to abort signals - This should do the trick.
import { CancelToken } from 'axios';
const getAllDemoBuckets = async (dispatch, cancelToken) => {
try {
const response = await axiosInstance.get('/demo/all', { cancelToken });
dispatch({ type: 'FETCH_SUCCESS', payload: response.data });
} catch (err) {
if ('isCancel' in err && err.isCancel()) {
return;
}
dispatch({ type: 'FETCH_ERROR' });
}
}
const MyComponent = () => {
useEffect(() => {
const source = CancelToken.source();
getAllDemoBuckets(dispatch, source.token);
return () => {
source.cancel();
};
}, []);
}

Why isn't this redux action promise resolving?

I have a redux action that returns a promise, so that the ui can respond accordingly based upon the result. This action sends a request to the backend, and when it gets a successful response from the server, it resolves.
redux action:
export const preauthorizePayment = (amount, connectedAccountId, customerId) => {
console.log("action reached")
return (dispatch) => {
dispatch({ type: types.UPDATING })
return new Promise((reject, resolve) => {
console.log("promise reached")
axios.get(PRE_AUTH_ENDPOINT, {
params: {
amount: amount,
customerId: customerId,
connectedAccountId: connectedAccountId
}
})
.then(res => {
console.log("then reached...")
if (res.data.result == 'success') {
dispatch({ type: types.UPDATE_SUCCESS });
console.log("if reached...");
return resolve();
} else {
console.log("else reached...")
console.log(JSON.stringify(res))
dispatch({
type: types.UPDATE_FAIL,
info: res.data.errorInfo,
errorCode: res.data.errorCode
})
return reject()
}
})
.catch(err => {
dispatch({ type: types.UPDATE_FAIL, errorInfo: err })
return reject()
})
})
}
}
the UI piece looks like:
props.preauthorizePayment(amount, connectedAccountId, customerId)
.then(() => {
console.log("pre-auth then reached")
// pre-auth success
setCheckInModal(false)
props.checkIn(props.evnt.id, props.role)
})
.catch(err => setCheckInModal(true))
What im experiencing is that everything is working properly up until the resolve() portion of the redux action. din the if block of the redux action's then(), the dispatch gets fired, the log fires, but it doesn't resolve. The UI piece's then() does not execute, no log, nothing. initially I didnt have the return keyword in the resolve line, and once I added it, it worked once. but then it stopped.
Whats going on?

Why during action call inn component occurs "Actions must be plain objects." - error?

In my project I have action call, service(for http fetch requests). In action I call service and after its executing I make dispatch in order to call reducer. In my component I call this action via mapDispatchToProps. On component loading I get error and dont quite understand why
http service:
export class HttpService {
static HEADERS = {
'Content-Type': 'application/json',
'Accept': 'application/json',
'Host': 'app.heavyrepair.ru',
'User-Agent': 'myAp'
};
static async get(url, requestParams) {
try {
return await request(url,'GET', requestParams)
} catch (e) {
console.log('REQUEST error. GET request error text: ', e)
throw e
}
}
static async post(url, requestParams) {
try {
return await request(url, 'POST', requestParams)
} catch (e) {
console.log('REQUEST error. POST request error text: ', e)
throw e
}
}
static async delete(url, requestParams) {
try {
return await request(url, 'DELETE', requestParams)
} catch (e) {
console.log('REQUEST error. DELETE request error text: ', e)
throw e
}
}
}
async function request(url, method = 'GET', requestParams) {
const config = {
method,
headers: HttpService.HEADERS,
};
if (method === 'POST') {
config.body = JSON.stringify(requestParams)
}
const response = await fetch(url, config);
return await response.json()
}
action:
export const fetchModels = () => (dispatch, getState) => {
dispatch({
type: REQUEST_MODELS,
payload: true,
});
const url = `http://localhost:8000/api/models`;
return HttpService.get(url)
.then(response => {
dispatch({
type: REQUEST_MODELS,
payload: false,
});
dispatch({
type: RECEIVE_MODELS,
payload: response,
});
});
};
and call action in component did mount:
class MainPage extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
this.props.fetchModels()
.then(this.setState({loading: false}));
}
render() {
const {
models,
modelsLoading
} = this.props;
return (
<div className="block-model-list">
...
</div>
);
}
}
const mapDispatchToProps = dispatch => (
{
fetchModels: () => dispatch(fetchModels()),
}
);
const mapStateToProps = state => {
return {
models : state.models,
modelsLoading : state.modelsLoading,
}
};
export default withRouter(connect(mapStateToProps, mapDispatchToProps)(MainPage));
error redux-logger.js?5d82:1 Uncaught Error: Actions must be plain objects. Use custom middleware for async actions. happens in didmount on action call. Why?
Actions in redux should be plain JavaScript objects. However by doing fetchModels: () => dispatch(fetchModels()) you are dispatching the result of fetchModels() function execution as an action.
In order to perform asynchronous operations inside the action creator, custom middleware has to be used. One such popularly used middleware is Redux Thunk . It will let you write async action creators.

How to fire a second function after a triggering a function in a functional component (React, React Hooks)

So I have a button:
<button onClick={doSomething}>Do it</button>
And I have a function
const doSomething = () => {
console.log(1)
}
And I want console.log(2) to fire after triggering doSomething.
Something like this:
const doSomething = () => {
console.log(1)
console.log(2)
}
But this one fires console.log(2) immediately. What I want is to fire console.log(2) after console.log(1), when clicking on the button.
Do I need to use useEffect() here? If yes, how?
EDIT:
Here is the problem. getQuestions() fires immediately when the function a runs. I want getQuestions() to fire after props.answerQuestion() finished.
const a = (e) => {
e.preventDefault();
props.answerQuestion({
question: question,
answer: answer,
});
getQuestions();
};
EDIT2:
export const answerQuestion = (answerQuestion) => (dispatch) => {
const token = localStorage.getItem("token");
if (token) {
axios
.post("http://localhost:5000/answerQuestion", answerQuestion, {
headers: {
"X-Auth-Token": token,
},
})
.then((res) => {
dispatch({
type: ANSWER_QUESTION,
payload: res.data,
});
});
}
};
You can JavaScript Promises for this problem. Using Promises in answerQuestion() Function will let you use to chain .then() and .catch() method in a function
export const answerQuestion = (answerQuestion) => (dispatch) => return new Promise((resolve, reject) => {
const token = localStorage.getItem("token");
if (token) {
axios
.post("http://localhost:5000/answerQuestion", answerQuestion, {
headers: {
"X-Auth-Token": token,
},
})
.then((res) => {
dispatch({
type: ANSWER_QUESTION,
payload: res.data,
});
resolve();
})
.catch((error) => {
reject(error);
})
}
});
const a = (e) => {
e.preventDefault();
props.answerQuestion({
question: question,
answer: answer,
})
.then(() => {
getQuestions();
})
.catch((error) => {
console.log(error)
})
};
You wouldn't need to use useEffect in this scenario, all you want to do is wait for the api call to resolve before calling getQuestions. one way you can accomplish this is by:
// update answerQuestion into an async function
export const answerQuestion = async (answerQuestion) => async (dispatch) => {
const token = localStorage.getItem("token");
if (token) {
const response = await axios // <--- add await here
.post("http://localhost:5000/answerQuestion", answerQuestion, {
headers: {
"X-Auth-Token": token,
},
})
await dispatch({
type: ANSWER_QUESTION,
payload: response.data,
});
}
};
then in your component:
const a = async (e) => {
e.preventDefault();
await props.answerQuestion({
question: question,
answer: answer,
});
getQuestions();
};

async function not waiting for the await to end

I'm trying to add an async/await in my code to have the app wait for the execution of a function to invoke an other one, but I can't seem to be able to figure out where my code is wrong.
I have an API call in a redux action, this is the action
export const editSecondaryCategory = (text) => {
return (dispatch) => {
let obj = {
text
};
axios
.put(
`${ROOT_URL}/...`,
obj
)
.then(() => {
dispatch({ type: EDIT_NOTE, payload: [...annotations] });
dispatch(loadAllAnnotations(cid, uuid));
})
.catch((error) =>
dispatch(
notifSend({
message: error.message,
kind: "danger",
dismissAfter: 2000,
})
)
);
};
};
I want, in my component, to wait after this action is completed to call an other function to update the state. Ideally, it should be something like this (I guess?):
async updateCategory() {
// do the thing I have to wait for
const propsUpdate = await this.props.editSecondaryCategory(text);
// if you've done the thing then call the function to update the state
if (updatedCat) {
this.updateStateArray();
}
}
And I'd call this.updateCategory() inside my component after the user is done editing the information.
Clearly, this code does not work. I know it's wrong, I just don't know why. Basically I have no clue what to write inside updateCategory() to make this work.
Please help lol
You need to rewrite editSecondaryCategory function to make it async.
export async function editSecondaryCategory(text){
return (dispatch) => {
let obj = {
text
};
axios
.put(
`${ROOT_URL}/...`,
obj
)
.then(() => {
dispatch({ type: EDIT_NOTE, payload: [...annotations] });
dispatch(loadAllAnnotations(cid, uuid));
})
.catch((error) =>
dispatch(
notifSend({
message: error.message,
kind: "danger",
dismissAfter: 2000,
})
)
);
};
};
Currently, your function is not an async function do the above changes and check.

Categories