simplify react function to get current user - reactjs

I am writing a function that using an ajax call to hit an API endpoint to get current user, it could be anonymous user or a actual one. the initial variable is set to true if it's used in constructor, false if it's used elsewhere. Don't know if this is the right way to do, or if the code is clean, for example: Is using initial to switch setState and this.state assignment the right way to do in react?
loadCurrentUser(initial = false) {
$.ajax({
type: 'GET',
url: 'accounts/api/users/current',
}).done((responseData) => {
const data = responseData.data;
const isAuthenticated = data.is_authenticated;
let stateData;
if (isAuthenticated) {
stateData = {
displayName: data.display_name,
reputationPoint: data.reputation_point,
};
} else {
stateData = {
displayName: null,
reputationPoint: null,
};
}
if (initial) {
this.state = stateData;
} else {
this.setState(stateData);
}
});
}

This can be simplified to this:
state = {displayName: null, reputationPoint: null}
componentWillMount() {
this.loadCurrentUser()
}
loadCurrentUser() {
$.ajax({
type: 'GET',
url: '/accounts/api/users/current',
}).done(responseData => {
const {is_authenticated, display_name, reputation_point} = responseData.data;
if (is_authenticated) {
this.setState({
displayName: display_name,
reputationPoint: reputation_point,
});
}
});
}
Mainly it's always a good idea to set the initial value in constructor level, let it be empty/null values. Then you can call setState from anywhere as necessary. Otherwise as the async AJAX request might get delyed, anywhere you reference the this.state.displayName or anything, will generate error.

Related

How do i stop a dependency from re-rendering infinite times in a useEffect?

I have a react-select multiselect component which is required to have preselected values based on the user role. The react-select component is used to filter data in a react-table.
I have 2 user roles - Dev and tester.
If it the dev, I need to have open and Reopened issues to be filtered on load
If it is a tester, I need to have resolved issues on load
This is a part of the code that i am using to achieve preselected
async function loadInfo() {
const body = {
project_id: projDetails.id,
};
const response = await axios({
method: "post",
url: apilist.dropdownData,
data: body,
})
.catch((err) => console.log(err));
if (response) {
const getData = response.data.data;
// console.log("IsGeneralInfo:", getData)
setGeneralInfo(getData);
let filteredenv = getData.status.filter((item) => item.id == 8 || item.id == 6)
let envfiltered = filteredenv.map((k) => {
return ({ label: k.values, value: k.values });
})
// console.log("envfilter", envfiltered);
// handleMultiStatusFilter(envfiltered);
}
}
// const {current:myArray}=useRef([{ label: 'Closed', value: 'Closed' }])
useEffect(() => {
if(envfilter){
let myArray=[{ label: 'Closed', value: 'Closed' },{ label: 'Reopen', value: 'Reopen' }];
handleMultiStatusFilter(myArray);
}
}, [selectedOptions])
const handleStatusFilter = (e) => {
setFilterValue(e);
if (e.length > 0) {
dispatch(filterByValue({ type: e, viewIssue: viewIssue, }))
}
else {
dispatch(showAllStatus({ type: 'All', viewIssue: viewIssue, }))
}
}
const handleMultiStatusFilter = (e) => {
setFiltered([])
let arr = []
e.map((data) => {
setFiltered(prevState => [...prevState, data.value]);
arr.push(data.value);
})
setSelectedOptions(e)
handleStatusFilter(arr)
}
This is a part of the redux code used for filtering
extraReducers: (builder) => {
// Add reducers for additional action types here, and handle loading state as needed
builder.addCase(fetchIssueList.fulfilled, (state, action) => {
// Add user to the state array
state.issuesList = {
status: 'idle',
data: action.payload.data.data !== undefined ? action.payload.data.data : [],
dataContainer: action.payload.data.data !== undefined ? action.payload.data.data : [],
no_of_records: action.payload.data.data !== undefined ? action.payload.data.data.length : 0,
error: {}
}
})
The code works fine with the filtering once i login, but the rerendering keeps going to infinite loop
Is there any way i could stop the infinite rerendering of code and have the filtering happen on load of the screen?
while working with dependencies in useEffect, try to use the most primitive part you can find. no complex objects, as they change way too fast.
for example: use the length of an array not the array itself.
even though for arrays it's mostly safe to use itself.
sorry. correction: for arrays it's not safe either. complex objects are compared by reference not by value. for that you need primitive types like number, string or boolean.

Why my setState returns an array, but the state is a promise when the component rerender?

The code below try to check if an url is reachable or not.
The urls to check are stored in a state called trackedUrls
I update this state with an async function checkAll.
The object just before being updated seems fine, but when the component rerender, it contains a promise !
Why ?
What I should change to my code ?
import React from "react"
export default function App() {
const [trackedUrls, setTrackedUrls] = React.useState([])
// 1st call, empty array, it's ok
// 2nd call, useEffect populate trackedUrls with the correct value
// 3rd call, when checkAll is called, it contains a Promise :/
console.log("trackedUrls :", trackedUrls)
const wrappedUrls = trackedUrls.map(urlObject => {
return (
<div key={urlObject.id}>
{urlObject.label}
</div>
)
})
// check if the url is reachable
// this works well if cors-anywhere is enable, click the button on the page
async function checkUrl(url) {
const corsUrl = "https://cors-anywhere.herokuapp.com/" + url
const result = await fetch(corsUrl)
.then(response => response.ok)
console.log(result)
return result
}
// Checks if every url in trackedUrls is reachable
// I check simultaneously the urls with Promise.all
async function checkAll() {
setTrackedUrls(async oldTrackedUrls => {
const newTrackedUrls = await Promise.all(oldTrackedUrls.map(async urlObject => {
let isReachable = await checkUrl(urlObject.url)
const newUrlObject = {
...urlObject,
isReachable: isReachable
}
return newUrlObject
}))
// checkAll works quite well ! the object returned seems fine
// (2) [{…}, {…}]
// { id: '1', label: 'google', url: 'https://www.google.Fr', isReachable: true }
// { id: '2', label: 'whatever', url: 'https://qmsjfqsmjfq.com', isReachable: false }
console.log(newTrackedUrls)
return newTrackedUrls
})
}
React.useEffect(() => {
setTrackedUrls([
{ id: "1", label: "google", url: "https://www.google.Fr" },
{ id: "2", label: "whatever", url: "https://qmsjfqsmjfq.com" }
])
}, [])
return (
<div>
<button onClick={checkAll}>Check all !</button>
<div>
{wrappedUrls}
</div>
</div>
);
}
Konrad helped me to grasp the problem.
This works and it's less cumbersome.
If anyone has a solution with passing a function to setTrackedUrls, I'm interested just for educational purpose.
async function checkAll() {
const newTrackedUrls = await Promise.all(trackedUrls.map(async urlObject => {
let isReachable = await checkUrl(urlObject.url)
const newUrlObject = {
...urlObject,
isReachable: isReachable
}
return newUrlObject
}))
setTrackedUrls(newTrackedUrls)
}
You can only put data into setState.

Delete data in state React Hooks

Maybe it is a dumb question, because I don't find any response on the net for this, but I'm trying to update my state after a axios.delete.
When I add data, I do this and it works fine :
const handleAddIncome = () => {
let incomeName = document.getElementById('newIncomeName').value
let incomeAmount = document.getElementById('newIncomeAmount').value
let data = {
[incomeName]: parseInt(incomeAmount)
}
axios({
method: "put",
url: `http://localhost:5000/api/balance/${uid}`,
withCredentials: true,
data: { incomes: { ...userWalletIncomes, ...data } }
}).then(() => {
setUserWalletIncomes({ ...userWalletIncomes, ...data })
})
}
I also added a bouton that delete the key/value, and this is where I'm stuck.
Here is what I tried, but no success :
const handleDeleteIncome = (key) => {
let data = { [key]: "" }
axios({
method: "delete",
url: `http://localhost:5000/api/balanceOneIncome/${uid}`,
data: data
}).then(() => {
data[key] = null
setUserWalletIncomes(...userWalletIncomes, data)
})
}
PS : the axios delete works fine, my database is updated normally. Not the state
Thanks for help !
[EDIT]
Here is my UseEffect :
useEffect(() => {
if (uid !== null) {
axios.get(`http://localhost:5000/api/balance/${uid}`)
.then((res) => {
if (res.data[0].incomes && res.data[0].fees) {
setUserWalletIncomes(res.data[0].incomes)
setUserWalletFees(res.data[0].fees)
} else if (res.data[0].incomes && !res.data[0].fees) {
setUserWalletIncomes(res.data[0].incomes)
setUserWalletFees({ 'cliquez-ici': '' })
} else if (!res.data[0].incomes && res.data[0].fees) {
setUserWalletIncomes({ 'cliquez-ici': '' })
setUserWalletFees(res.data[0].fees)
}
})
}
}, [uid])
For those who need, I finally chose to change my controller in my backend and make it send the new object after the delete.
I just take that response, and set the new state.
Thanks
Because you already have setUserWalletIncomes({ ...userWalletIncomes, ...data }), I expect userWalletIncomes is an object like { name1: value1, name2: value2 }, right?
Then you have setUserWalletIncomes(...userWalletIncomes, data), use array spread on an object is a runtime error because it's not iterable:
let a = { foo: 42 };
console.log(...a); // TypeError: Found non-callable ##iterator
I guess you still wanted to write this:
let data = { [key]: "" };
setUserWalletIncomes({ ...userWalletIncomes, ...data });
But this only sets the key field to an empty string. To remove the field, you can combine the answer from How do I remove a property from a JavaScript object?
const temp = {...userWalletIncomes};
delete temp[key];
setUserWalletIncomes(temp);

Object is possibly undefined even with if statement

I've got a Typescript error TS2532: Object is possibly 'undefined'. I'm creating an SPA using Typescript, and on one of the pages, then you have the possibility of uploading a file. The backend is already written, so the file stored in state will be made into a binary code which the server side reads. The file setstate is set to undefined.
I've done a few searches, but the solutions don't seem to work for me. The first solution was to create an if statement:
if (this.state.uploadFile !== undefined) {
const terms = this.state.uploadFile;
// Logic
}
Whenever terms is used in the logic portion, then I get the error mentioned above.
The other solution is to tell the compiler that it is definetly defined:
const terms = this.state!.uploadFile;
This still throws the same error, and thought I might have placed the ! in the wrong place, but when I move it to const terms = this.state.termSheet!; then that causes a new error when calling terms.getAsBinary() I get the error Property 'getAsBinary' does not exist on type 'never'
Code in context:
// Imports
class SubmitIssue extends React.Component<StylesProps> {
state = {
alert: false,
amount: '',
uploadFile: undefined,
verify: false,
}
handleAmountChange(e) {
this.setState({ amount: e.target.value });
}
handleFileChange(e) {
this.setState({ uploadFile: e.target.files[0] });
}
handleVerifyChange() {
this.setState({ verify: !this.state.verify });
}
handleClick() {
const config = { headers: { 'Content-Type': 'multipart/form-data' } };
const bodyFormData = new FormData();
bodyFormData.set('Amount', this.state.amount);
bodyFormData.set('uploadFile', this.state.termSheet.getAsBinary());
bodyFormData.set('Verify', this.state.verify.toString());
axios.post(
`${process.env.API_URL}RegisterIssue/Create`,
bodyFormData,
config
).then(res => {
console.log(res);
this.setState({ alert: true }, function() { this.sendData(); });
}).catch((error) => {
console.log(error);
})
}
sendData() {
const alertState = this.state.alert;
this.props.parentAlertCallback(alertState);
}
render() {
return (
// Design
);
}
}
export default withStyles(styles)(SubmitIssue);
So, I'm a little stumped as to what the correct way to handle this error is.
It is because you check only state.uploadFile but use state.termSheet.
There are two possible solutions:
You cat set a default value (in case terms is not defined):
const terms = this.state.termSheet ? this.state.termSheet : defaultValue;
Or you check the terms in the if statement, too:
if (this.state.uploadFile && this.state.termSheet)

Insert data into a dynamic array in Vue / Vuex

I have the following situation: when executing a change on select I am firing a function that goes to an API and performs a search. The result of this search is a JSON. After executing the search I am trying to get some specific ids, however in my component I am not able to access them.
I'm a beginner, I apologize for mistakes and lack of standards.
Here's my code where I run the #change on page:
<select v-if="users.items" v-model="usuarioId" #change="getById(usuarioId)">
<option value="" disabled selected>Escolha um Usuário</option>
<option v-for="user in users.items" :key="user.id" :value="user.id">{{user.nome}}</option>
</select>
GetById in Module:
import { usuarioSistemaService } from '../_services';
const state = {
all: {}
};
const actions = {
getById({ commit }, id){
commit('getByIdRequest', id);
usuarioSistemaService.getById(id)
.then(
usuarioSistemas => commit('getByIdSuccess', usuarioSistemas),
error => commit('getByIdFailure', error)
);
}
};
const mutations = {
getByIdRequest(state) {
state.all = { loading: true };
},
getByIdSuccess(state, usuarioSistemas) {
state.all = { items: usuarioSistemas };
},
getByIdFailure(state, error) {
state.all = { error };
}
};
export const usuarioSistemas = {
namespaced: true,
state,
actions,
mutations
};
GetById in Service:
function getById(id) {
const requestOptions = {
method: 'GET',
headers: authHeader()
};
return fetch(`${config.apiUrl}/usuariosistema/${id}`, requestOptions).then(handleResponse);
}
function handleResponse(response) {
return response.text().then(text => {
const data = text && JSON.parse(text);
if (!response.ok) {
if (response.status === 401) {
// auto logout if 401 response returned from api
logout();
location.reload(true);
}
const error = (data && data.message) || response.statusText;
return Promise.reject(error);
}
return data;
});
}
With the result I want to insert data (usuarioSistema.sistemaId) into this array -> systemId:
<script>
import { mapState, mapActions } from 'vuex'
export default {
data () {
return {
usuarioId: '',
sistemaId: [],
}
}
}
I tried to create a javascript function in "methods:" for this, but the object always comes empty. I also tried to create something invisible on the page to feed this array, but it did not work.
Could you help me, please?
Thanks
You can access store data in a component via the computed object and Vuex's mapGetters helper function:
https://vuex.vuejs.org/guide/getters.html#the-mapgetters-helper
Assuming that you have already performed the API call, you can do it like this in your component:
computed: {
...mapGetters({
'usuarioSistemas': 'usuarioSistemas/all'
})
}
In the code, you should able to access it via this.usuarioSistemas (it is an alias for usuarioSistemas/all).

Resources