Store updates but component does not - reactjs

I'm trying to update the component after a user inputs new data.
Currently on componetDidMount() I call my reducer to fetch data from an API and return it to the component. That works. But when the user updates add a new form and it gets saved in the API, I call the API and the store updates (both redux and console log confirmed this) but the component does not update.
I'm think this could be an aysnc problem but I'm not certain.
Store:
type KnownAction = RecievedInvoicesAction | RequestInvoicesAction | RefreshInvoices;
export const actionCreators = {
requestInvoices: (): AppThunkAction<KnownAction> => (dispatch, getState) => {
const appState = getState();
if (appState && appState.invoices && appState.invoices.isLoading) {
fetch('https://localhost:44304/api/invoices')
.then((response) => response.json())
.then((data) => {
dispatch({
type: 'RECIEVED_INVOICES',
invoices: data,
isLoading: false,
});
toast.success('Invoices loaded đź‘Ť', {
position: "bottom-right",
autoClose: 5000,
hideProgressBar: false,
closeOnClick: true,
pauseOnHover: true,
draggable: true,
progress: undefined,
})
});
dispatch({ type: 'REQUEST_INVOICES', isLoading: true});
}
},
refreshInvoices: (): AppThunkAction<KnownAction> => (dispatch) => {
fetch('https://localhost:44304/api/invoices')
.then((response) => response.json())
.then((data) => {
console.log(data);
dispatch({
type: 'REFRESH_INVOICES',
invoices: data,
isLoading: false,
});
});
dispatch({ type: 'REQUEST_INVOICES', isLoading: true});
}
};
// REDUCER
const unloadedState: InvoiceState = { isLoading: true, invoices: [] };
export const reducer: Reducer<InvoiceState> = (
state: InvoiceState | undefined,
incomingAction: Action
): InvoiceState => {
if (state === undefined) {
return unloadedState;
}
const action = incomingAction as KnownAction;
switch (action.type) {
case 'REQUEST_INVOICES' :
return Object.assign({}, state, {
isLoading: action.isLoading
})
case 'RECIEVED_INVOICES':
return Object.assign({}, state, {
invoices: action.invoices,
isLoading: action.isLoading
})
case 'REFRESH_INVOICES':
return Object.assign({}, state, {
invoices: action.invoices,
isLoading: action.isLoading
})
default:
return state;
}
};
Main Component:
class Home extends React.Component<HomeProps, State> {
constructor(SearchInvoiceProps : HomeProps) {
super(SearchInvoiceProps);
this.state = {
queryText : '',
filterBy : 'all',
orderBy : 'asc',
order : 'invoiceDate',
error : '',
invoicesArr : []
}
}
componentDidMount() {
this.ensureDataFetched();
this.setState({
invoicesArr : this.props.invoices
})
}
ensureDataFetched = () => {
this.props.requestInvoices();
}
...
}
export default connect(
(state: ApplicationState) => state.invoices,
InvoiceStore.actionCreators
)(Home as any);

Related

React-Redux conditional variable

I'm struggling with react-redux variable for hours...hope someone can help.
The conditional returns to me that the variable order.name is not defined, although everything goes as it should in the reducer and action.
When isLoading === true, it continues rendering {order.name} and I know it is not defined at that point because it takes some time. At that time i set Loader to do it's job..
So it’s not clear to me why he continues to render even though there’s a conditional one that shouldn’t allow it... until isLoading === false.
Here is console.log of orderDetails
import { getOrderDetailsAction } from "../actions/orderAction";
const OrderScreen = ({ match }) => {
const orderId = match.params.id;
const dispatch = useDispatch();
useEffect(() => {
dispatch(getOrderDetailsAction(orderId));
}, [dispatch, orderId]);
const orderDetails = useSelector((state) => state.orderDetails);
const { order, isLoading } = orderDetails;
return isLoading ? <Loader /> : <>{order.name}</>;
};
export default OrderScreen;
Reducer
export const orderDetailsReducers = (
state = { isLoading: true, orderItems: [], shippingAddress: {} },
action
) => {
switch (action.type) {
case ORDER_DETAILS_REQUEST:
return {
...state,
isLoading: true,
};
case ORDER_DETAILS_SUCCESS:
return {
isLoading: false,
order: action.payload,
};
case ORDER_DETAILS_FAILED:
return {
isLoading: false,
error: action.payload,
};
default:
return { state };
}
};
Action
export const getOrderDetailsAction = (id) => async (dispatch, getState) => {
try {
dispatch({
type: ORDER_DETAILS_REQUEST,
});
//Getting TOKEN
const {
userLogin: { userInfo },
} = getState();
//Passing TOKEN
const config = {
headers: {
"auth-token": `${userInfo.token}`,
},
};
const { data } = await axios.get(`/api/orders/${id}`, config);
dispatch({
type: ORDER_DETAILS_SUCCESS,
payload: data,
});
} catch (error) {
dispatch({
type: ORDER_DETAILS_FAILED,
payload: error.response.data.msg,
});
}
};
Check redux action for how isLoading state is changed from redux dev tools
Check reducer name -> state.orderDetails (does this exists ?)
const orderDetails = useSelector((state) => state.orderDetails);
Also, we can correct this
state = { isLoading: true, orderItems: [], shippingAddress: {}, order: {} }
// return entire state and not just isLoading and order
case ORDER_DETAILS_SUCCESS:
return {
...state, <--------
isLoading: false,
order: action.payload,
};
case ORDER_DETAILS_FAILED:
return {
...state, <---------
isLoading: false,
error: action.payload,
};

in order to be iterable non-array objects must have a symbol.iterator () method react native

Problem:
In my React native application, I have created redux-thunk action to access the user's current location. But when Running it give me this error called in order to be iterable non-array objects must have a symbol.iterator () method. This is how I have wrote my actions.
import Geolocation from 'react-native-geolocation-service';
export const GET_CURENT_POSITION = 'location/GET_CURENT_POSITION';
export const GET_CURENT_POSITION_SUCCESS =
'location/GET_CURENT_POSITION_SUCCESS';
export const GET_CURENT_POSITION_FAILED = 'location/GET_CURENT_POSITION_FAILED';
const getCurrentPosition = (payload) => ({
type: GET_CURENT_POSITION,
payload,
});
const getCurrentPositionSuccess = (response) => ({
type: GET_CURENT_POSITION_SUCCESS,
response,
});
const getCurrentPositionFailed = (response) => ({
type: GET_CURENT_POSITION_FAILED,
response,
});
const getposition = () => {
return (dispatch) => {
dispatch(getCurrentPosition({msg: 'startgetting'}));
return Geolocation.getCurrentPosition(
(position) => {
dispatch(getCurrentPositionSuccess(position));
},
(error) => {
dispatch(getCurrentPositionFailed(error));
},
{enableHighAccuracy: true, timeout: 15000, maximumAge: 10000},
);
};
};
export default {
getposition: getposition,
};
This is how I have wrote reducers.
import {locationTypes as types} from '_store/actions';
const location = (
state = {
hasError: false,
errorCode: '',
error: '',
location: '',
},
payload,
) => {
switch (payload.type) {
case types.GET_CURENT_POSITION:
return {
...state,
hasError: false,
error: '',
location: '',
};
case types.GET_CURENT_POSITION_SUCCESS:
return {
...state,
hasError: false,
location: payload,
};
case types.GET_CURENT_POSITION_FAILED:
return {
...state,
hasError: false,
error: payload,
};
default:
return state;
}
};
export default location;
This is how I have dispatched the function in connect.
const mapDispatchToProps = (dispatch) => {
return {
createpatient: (patient) => dispatch(patientActions.createpatient(patient)),
getposition: () => dispatch(locationActions.getposition()),
};
};
I tried a lot to figure out what is wrong but I was unable to do so. Can someone help me with this problem? Thank you very much.

React, Typescript, Hooks - destructuring data from hook, TS2339: Property 'data' does not exist on type

I have problem with destructuring Typescript data object from my own Hook created in React.
export interface InitialState {
pokemonListLoading: false;
pokemonListLoadingFailed: false;
data: [];
}
interface FetchPokemonList {
type: typeof FETCH_POKEMON_LIST;
}
interface FetchPokemonListSuccess {
type: typeof FETCH_POKEMON_LIST_SUCCESS;
payload: PokemonList;
}
...
export type PokemonListActionTypes = FetchPokemonList | FetchPokemonListSuccess | FetchPokemonListError;
const dataFetchReducer = (state: InitialState, action: PokemonListActionTypes) => {
switch (action.type) {
case FETCH_POKEMON_LIST:
return {
...state,
pokemonListLoading: true,
pokemonListLoadingFailed: false,
};
case FETCH_POKEMON_LIST_SUCCESS:
return {
...state,
pokemonListLoading: false,
pokemonListLoadingFailed: false,
data: action.payload,
};
case FETCH_POKEMON_LIST_ERROR:
return {
...state,
pokemonListLoading: false,
pokemonListLoadingFailed: true,
};
default:
throw new Error();
}
};
export const fetchPokemonList = (initialUrl: string, initialData: []) => {
const [url, setUrl] = useState(initialUrl);
const [state, dispatch] = useReducer(dataFetchReducer, {
pokemonListLoading: false,
pokemonListLoadingFailed: false,
data: initialData,
});
useEffect(() => {
const fetchData = async () => {
dispatch({ type: FETCH_POKEMON_LIST });
try {
const result = await axios(url);
dispatch({ type: FETCH_POKEMON_LIST_SUCCESS, payload: result.data });
} catch (error) {
dispatch({ type: FETCH_POKEMON_LIST_ERROR });
}
};
fetchData();
}, [url]);
return [state, setUrl];
};
and whole component
import React, { FunctionComponent } from 'react';
import { fetchPokemonList, InitialState } from '../../hooks/fetchPokemonList';
const PokemonList: FunctionComponent = () => {
const [{
data: { results: pokemonList },
pokemonListLoading,
pokemonListLoadingFailed,
},
] = fetchPokemonList('https://pokeapi.co/api/v2/pokemon',[]);
return (
<div>
PokemonList
{pokemonListLoading ? (
<div>Laoding...</div>
) : (
pokemonList && pokemonList.map((pokemon: { name: string}) => (
<div key={pokemon.name}>{pokemon.name}</div>
))
)}
{pokemonListLoadingFailed && <div>Error</div>}
</div>
)
}
export { PokemonList }
error code displayed by Webstorm
TS2339: Property 'data' does not exist on type '{ pokemonListLoading:
boolean; pokemonListLoadingFailed: boolean; data: []; } | {
pokemonListLoading: boolean; pokemonListLoadingFailed: boolean; data:
PokemonList; } | Dispatch ...>>'.
The issue is within this line:
dispatch({ type: FETCH_POKEMON_LIST_SUCCESS, payload: result.data });
Where you send as payload without using a key for your new data value.
Then in the code section you're setting data with the payload object, which results in the error you're experiencing:
case FETCH_POKEMON_LIST_SUCCESS:
return {
...state,
pokemonListLoading: false,
pokemonListLoadingFailed: false,
data: action.payload,
};
Try passing your payload like this: payload: { data: result.data }.
Then set your data respectively: data: action.payload.data

Reducer updates state object which it should not be able to access

The problem is that when on of my reducer updates its own state, it also updates the state of another reducer.
//authActions.js
export const authActions = {
login: (props) => dispatch => {
// This will make sure the loading spinner will appear.
dispatch({
type: POST_LOGIN_PENDING,
payload: null
})
// make request to login user
axios.post(LOGIN_ENDPOINT, {
email: props.email,
password: props.password
}).then(res => dispatch({
type: POST_LOGIN_FULFILLED,
payload: res.data
})
).catch( () => dispatch({
type: POST_LOGIN_REJECTED,
payload: null
}))
},
logout: () => dispatch => {
dispatch({
type: LOGOUT,
payload: null
})
},
// authReducer.js
export const initialState = {
token: "",
userRole: "",
isLoading: false,
loginFailed: false,
isAuthenticated: false,
}
export function authReducer(state = initialState, action) {
switch (action.type) {
case POST_LOGIN_PENDING:
return {
...state,
isLoading: true,
}
case POST_LOGIN_FULFILLED:
return {
...state,
token: action.payload.token,
userRole: action.payload.userRole,
loginFailed: false,
isAuthenticated: true,
isLoading: false,
}
case POST_LOGIN_REJECTED:
return {
...state,
loginFailed: true,
isLoading: false,
}
// studentActions.js
export const studentActions = {
getAllStudents: props => dispatch => {
dispatch({
type: GET_ALL_STUDENTS_PENDING,
payload: null,
})
axios.get(STUDENTS_ENDPOINT, {
headers: {
'Authorization': `Bearer ${props.token}`
}
})
.then(res =>
dispatch({
type: GET_ALL_STUDENTS_FULFILLED,
payload: res.data
}))
.catch(err => dispatch({
type: GET_ALL_STUDENTS_FULFILLED,
payload: err
}))
},
// studentReducer.js
export const initialState = {
students: [],
err: "",
isLoading: false,
}
export function studentReducer(state = initialState, action) {
switch (action.type) {
case GET_ALL_STUDENTS_PENDING:
return {
...state,
isLoading: true,
}
case GET_ALL_STUDENTS_FULFILLED:
return {
...state,
students: action.payload,
isLoading: false,
}
case GET_ALL_STUDENTS_REJECTED:
return {
...state,
err: action.payload,
isLoading: false,
}
case DELETE_STUDENT_BY_ID_FULFILLED:
return state
default:
return state
}
}
When a user logs in and the POST_LOGIN_FULFILLED applies. I would expect only the initialstate of the authReducer to be updated, but when inspect with the redux devtools I can see that that the array "studens" which is part of the initialstate of the studentReducer also is updated. From what I understand this should not be possible.
After the user has logged in the students array is filled: (From redux devtools)
student: {
students: [] => {....some stuff}
isLoading: true => false
}
By reading you comments it looks like that GET_ALL_STUDENTS_FULFILLED refers to POST_LOGIN_FULFILLED . This must be the reason why your students array is updated. Change
export const GET_ALL_STUDENTS_PENDING = 'POST_LOGIN_PENDING';
export const GET_ALL_STUDENTS_REJECTED = 'POST_LOGIN_REJECTED';
export const GET_ALL_STUDENTS_FULFILLED = 'POST_LOGIN_FULFILLED';
to
export const GET_ALL_STUDENTS_PENDING = 'GET_ALL_STUDENTS_PENDING ';
export const GET_ALL_STUDENTS_REJECTED = 'GET_ALL_STUDENTS_REJECTED ';
export const GET_ALL_STUDENTS_FULFILLED = 'GET_ALL_STUDENTS_FULFILLED ';
Action types should be unique or else it might get triggered by some other action

React Router URL

In route.jsx my path looks like:
<Route path='/my-work/:workName' component={WorkShow} />
My link looks like:
<Link to={`/my-work/${props.work.name}`}>
I want to display the name in the url, but pass the id into the params instead so that I can fetch this in my component and pass it into the api.
My api path takes in the ID as a parameter but I want to avoid displaying this in the URL.
How would I store this as a key value pair....
Here is my reducer.js:
import Constants from '../constants';
const initialState = {
projects: [],
project: [],
fetching: true,
};
export default function reducer(state = initialState, action = {}) {
switch (action.type) {
case Constants.PROJECTS_FETCHING:
return Object.assign({}, state, { fetching: true });
case Constants.PROJECTS_RECEIVED:
return Object.assign({}, state, { projects: action.proj, fetching: false });
case Constants.PROJECT_FETCHING:
return Object.assign({}, state, { fetching: true });
case Constants.PROJECT_RECEIVED:
return Object.assign({}, state, { project: action.project, fetching: false });
default:
return state;
}
}
And here is my action.js:
import Request from 'superagent';
import Constants from '../constants';
const Actions = {
fetchProjects: () => (dispatch) => {
dispatch({ type: Constants.PROJECTS_FETCHING });
Request.get('/api/v1/projects')
.then((data) => {
dispatch({
type: Constants.PROJECTS_RECEIVED,
proj: data.body,
});
});
}, fetchProject: projectId => (dispatch) => {
dispatch({ type: Constants.PROJECT_FETCHING });
Request.get('/api/v1/projects/' + projectId)
.then((data) => {
dispatch({
type: Constants.PROJECT_RECEIVED, project: data.body,
});
});
},
};
export default Actions;

Resources