Cant access props and state within function in React class Component - javascript

I am trying to get values from my state and props in function. This function is fired under componentWillReceiveProps method. However when i try to console log my props and state values under that function i get 'undefined' as result. Is there something to do with my state not updating fast enough? Am i missing to pass props somewhere? Where could lay error and how could i get my function to handle correct values?
handleShow() function:
state = {
training: {},
isCreator: null
}
componentWillReceiveProps(newProps) {
console.log(newProps);
if (newProps.token) {
axios.defaults.headers = {
"Content-Type": "application/json",
Authorization: newProps.token
}
const trainingID = this.props.match.params.trainingID;
axios.get(`http://127.0.0.1:8000/api/${trainingID}/`)
.then(res => {
this.setState({
training: res.data
});
console.log('treener', res.data.coach)
})
.catch(err => {console.log(err)})
this.handleShow()
} else{}
}
handleShow() {
if(this.props.token == this.state.training.coach) {
console.log('Props:', this.props.token);// value of undefined
console.log('State.training', this.state.training.coach)//
this.setState({
isCreator: true
})
}else{
this.setState({
isCreator: null
})
}
}

Related

useEffect and setState with hook function

I want to try and use react to fetch data efficiently using useEffect appropriately.
Currently, data fetching is happening constantly, instead of just once as is needed, and changing when there is a input to the date period (calling different data).
The component is like this,
export default function Analytics() {
const {
sentimentData,
expressionsData,
overall,
handleChange,
startDate,
endDate,
sentimentStatistical,
} = useAnalytics();
return (
UseAnalytics is another component specifically for fetching data, basically just a series of fetches.
i.e.,
export default function useAnalytics() {
....
const { data: sentimentData } = useSWR(
`dashboard/sentiment/get-sentiment-timefilter?startTime=${startDate}&endTime=${endDate}`,
fetchSentiment
);
....
return {
sentimentData,
expressionsData,
overall,
handleChange,
setDateRange,
sentimentStatistical,
startDate,
endDate,
};
}
Thanks in advance,
The apirequest is like this,
export async function apiRequest(path, method = "GET", data) {
const accessToken = firebase.auth().currentUser
? await firebase.auth().currentUser.getIdToken()
: undefined;
//this is a workaround due to the backend responses not being built for this util.
if (path == "dashboard/get-settings") {
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
if (response.error === "error") {
throw new CustomError(response.code, response.messages);
} else {
return response;
}
});
}
return fetch(`/api/${path}`, {
method,
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${accessToken}`,
},
body: data ? JSON.stringify(data) : undefined,
})
.then((response) => response.json())
.then((response) => {
if (response.status === "error") {
// Automatically signout user if accessToken is no longer valid
if (response.code === "auth/invalid-user-token") {
firebase.auth().signOut();
}
throw new CustomError(response.code, response.message);
} else {
return response.data;
}
});
}
I think using useEffect here is the right approach. i.e.,
useEffect(()=>{
// this callback function gets called when there is some change in the
// state variable (present in the dependency array)
},[state variable])
I'm confused about how to update the constants properly, something like this seems like one approach, but not sure about how I can use useEffect to update these variables properly, or if I should be doing this inside of useAnalytics?
i.e.,
const [analytics, setAnalytics] = useState({
sentimentData: {},
expressionsData: {},
overall: {},
handleChange: () => {},
startDate: '',
endDate: '',
sentimentStatistical:{},
});
useEffect(()=>{
// this callback function gets called when there is some change in the
// state variable (present in the dependency array)
},[state variable])
const {
sentimentData,
expressionsData,
overall,
handleChange,
startDate,
endDate,
sentimentStatistical,
} = useAnalytics();
Realised SWR is a hook, need to use SWR documentation :P
You have to store the requested information in states inside your custom hook. Then you could consume this hook wherever you want. This should work.
Define custom hook
const useAnalitycs = () => {
const [analytics, setAnalytics] = useState({
sentimentData: {},
expressionsData: {},
overall: {},
startDate: '',
endDate: '',
sentimentStatistical:{},
});
const handleChange = () => {
/* */
};
useEffect(() => {
const fetchData = async () => {
// const response = await apiCall();
// setAnalytics(...)
};
fetchData();
}, []); // called once
return {
...analytics,
handleChange
};
};
Consume useAnalytics hook
const ComponentConsumerA = () => {
/*
const { state/methods you need } = useAnalytics()
...
*/
};
const ComponentConsumerB = () => {
/*
const { state/methods you need } = useAnalytics()
...
*/
};

Having problem handling errors in react-redux white attempting to SignUp

Here i have my component code for SignIng Up user and check for Error. At first error is null.
let error = useSelector((state) => state.authReducer.error);
const checkErrorLoading = () => {
console.log("If error found"); //At first it gives null, but on backend there is error
toast.error(error);
console.log(loading, error);
};
const handleSubmit = async (e) => {
if (isSignup) {
dispatch(signup(form, history));
checkErrorLoading();
} else {
dispatch(signin(form, history));
checkErrorLoading();
}
};
Now at my singupForm, i provide wrong input or wrong data. The backend gives me error that is completely fine.
ISSUE => But when i click on Login button. At first attempt it does not provide any error message. After second attempt it works fine, but not at first attempt. At first attempt it gives me Error value NULL while there is still an error
Here is my action.
export const signup = (formData, history) => async (dispatch) => {
try {
const res = await api.signUp(formData);
dispatch({ type: authConstants.AUTH_REQUEST });
if (res.status === 200) {
const { data } = res;
console.log(data);
dispatch({
type: authConstants.AUTH_SUCCESS,
payload: data,
});
}
console.log(res.status);
history.push("/");
} catch (error) {
console.log(error.response);
dispatch({
type: authConstants.AUTH_FAILURE,
payload: error.response.data.error,
});
}
};
and than reducer.
const initialState = {
authData: null,
error: null,
loading: false,
};
const authReducer = (state = initialState, action) => {
switch (action.type) {
case authConstants.AUTH_REQUEST:
return { ...state, loading: true, error: null };
case authConstants.AUTH_SUCCESS:
localStorage.setItem("profile", JSON.stringify({ ...action?.payload }));
return { ...state, authData: action?.data, loading: false, error: null };
case authConstants.AUTH_FAILURE:
console.log(action.payload);
return { ...state, loading: false, error: action.payload };
}
You should use useEffect instead of local function (checkErrorLoading ) for such cases:
useEffect(() => {
console.log("If error found");
toast.error(error);
console.log(loading, error);
},[error]);
Currently what you doing is creating local function that closures error variable, which initially is null + state is updated asynchronously, so you cannot execute function right after dispatching (even if variable wouldn't be closured, you will not have fresh state there)

React - Loading data before render

I'm new to react and I have a question about a best practice that sees me make a mistake .
I call an API to retrieve information and modify an array in the state once the response is returned by the API. In the "render" I have to retrieve the information from this array (when it is completed) or it sends me back an error because the array is empty when the render is initialized.
class MyClass extends React.Component {
constructor(props) {
super(props)
this.state = {
activeIndex: 0,
items: []
}
}
componentDidMount() {
axios
.get(`API_ADDRESS`, {
headers: {
Authorization: `Token XXX`,
},
})
.then(function(response) {
this.setState({
items: response.results,
})
})
.catch(error => {
notification.warning({
message: error.code,
description: error.message,
})
})
}
changeDialog = (e, index) => {
e.preventDefault()
this.setState({
activeIndex: index,
})
}
render() {
const { activeIndex, items } = this.state
const {
first_name: firstName,
last_name: lastName,
phone,
email,
address,
} = items[activeIndex]
The error indicates :
TypeError: _items$activeInde is undefined
How can I solve this error related to data loading? (trying to keep the destrying elements method)
Thanks a lot
Eliott
Because API that you fetch from server is async. The first time render of Component, data that you setState in axios still not yet updated, it just updated when Component render the second time.
So you must check state in render Component like this to make sure that if activeIndex is defined then declare variable with items[activeIndex] :
activeIndex && const {
first_name: firstName,
last_name: lastName,
phone,
email,
address,
} = items[activeIndex]
Two issues:
beware of this inside the Promise returned by axios. You use function(){} so the this inside is not the component's instance. Change it to an arrow function.
add a guard so you won't destructure undefined when activeIndex points to an item element that is not there (which happens in the initial loading before the axios fetches the data).
Fix:
// ... (code not shown remains unmodified)
componentDidMount() {
axios
.get(`API_ADDRESS`, {
headers: {
Authorization: `Token XXX`,
},
})
.then(response => { // changed this line
this.setState({
items: response.results,
})
})
// ... (code not shown remains unmodified)
render() {
const { activeIndex, items } = this.state
if (!items[activeIndex]) { // added this line
return <div>Hold tight while items are being fetched...</div>; // added this line
} // added this line
const {
first_name: firstName,
// ... (code not shown remains unmodified)
just change your component like so:
constructor(props) {
super(props)
this.state = {
activeIndex: 0,
items: [],
isFetching: false
}
}
componentDidMount() {
// staring your fetching
this.setState({isFetching: true});
axios
.get(`API_ADDRESS`, {
headers: {
Authorization: `Token XXX`,
},
})
.then(function(response) {
// finish fetching when your response is ready :)
this.setState({
items: response.results,
isFetching: false
});
})
.catch(error => {
// finish fetchnig
this.setState({isFetching: false})
notification.warning({
message: error.code,
description: error.message,
})
})
}
changeDialog = (e, index) => {
e.preventDefault()
this.setState({
activeIndex: index,
})
}
render() {
// if your component is while fetching shows a loading to the user
if(this.state.isFetching) return <div>Loading...</div>;
// if there is no results shows a msg to the user
if(this.state.items.length === 0) return <div>there is not items!!!</div>
const { activeIndex, items } = this.state
const {
first_name: firstName,
last_name: lastName,
phone,
email,
address,
} = items[activeIndex]

Having issue with state variable in react js. Cannot update the variable's value to true

import getAuthentication from './getAuthentication';
class Home extends React. Component {
constructor() {
super();
//this.authentication = false;
this.state = {
username: '',
password: '',
check:false,
authentication:false
};
this.err = '';
}
componentDidUpdate() {
console.log (this.state.authentication);
console.log(this.state.authentication == true);
if (this.state.check)
{
const promiseAuthentication = getAuthentication(
this.state.username,
this.state.password,
);
promiseAuthentication
.then(response => {
console.log (response.data.Success);
console.log(response.data.Success == true);
this.setState({check :false, authentication:response.data.Success});
})
.catch(error => {
// console.log(error);
this.err = error;
});
}
if (this.state.authentication == true) {
event.preventDefault();
history.push('/overview');
}
}
assignUsername = event => {
this.setState({ username: event.target.value });
};
assignPassword = event => {
this.setState({ password: event.target.value });
};
handleSubmit = () => {
this.setState({ check:true });
};
==============================================================
getAuthentication.js
import axios from 'axios';
function getAuthentication(username, password) {
const authenticationConfig = {
Email: username,
Password: password,
};
return axios.post(
'http://localhost:5002/login/confirmation',
authenticationConfig,
);
}
export default getAuthentication;
In the above code my this.state.Authentication is not getting updated to true
I am trying to update its value in axios promise.
Can someone please tell me what's wrong? I mean I have tried everything but I am not able to proceed.
How do I change the state of Authentication object and switch new window?
I have a second file that is returning the axios promise where promise value is "undefined".. How do I make async call and resolve this issue ??
componentDidUpdate is wrapped in if (this.state.check). Nothing in the code you pasted sets this.state.check to true. Set this.state.check: true.

React: Can't setState in promise

I am very confused about this. I am trying to set the isAuthenticated state to true after verifying a JWT, and it won't work. The console logs are outputting weird things (screenshot below).
I suspect I am messing something up with the fetch promise, but I don't know what.
class App extends React.Component {
constructor(){
super()
this.state = {
isAuthenticated: false
}
}
componentWillMount(){
this.authenticate()
}
authenticate(){
fetch("http://localhost:3000/authenticated", {
method: "POST",
headers: {
"Accept": "application/json",
"Content-type": "application/json"
},
body: JSON.stringify({
token: localStorage.getItem("token")
})
})
.then(res => res.json())
.then(data => {
console.log(data); // Server returns true
console.log(this.state); // isAuthenticated: false
console.log(this); // isAuthenticated: true
if(data.success){
this.setState({
isAuthenticated: true
}, console.log(this.state)) // isAuthenticated: false
}
})
.catch(err => {
console.log(err);
});
}
The console logs:
React doesn't bind automatically this to class methods.
You have two ways to handle this.
on constructor
constructor(){
super()
this.state = {
isAuthenticated: false
}
this.authenticate = this.authenticate.bind(this)
}
or a bit cleaner ES6 way and use arrow functions
authenticate = () => {
[your code]
this.setState({ isAuthenticated: true }) // example
}

Categories