Seen some similiar posts, haven't found my answer. I have this code trying to delete an object from an array, with the backend set up so it works on Post Man, when I started working with Redux I encountered this issue.
action.js:
export const deletePost = (id) => async (dispatch) => {
try {
await axios.delete(`api/posts/${id}`);
dispatch({
type: DELETE_POST,
payload: { id },
});
dispatch(setAlert('Post removed', 'success'));
} catch (err) {
dispatch({
type: POST_ERROR,
payload: { msg: err.response.statusText, status: err.response.status },
});
}
};
Reducer has been combined in the root reducer file
Reducer.js:
case DELETE_POST:
return {
...state,
posts: state.posts.filter((post) => post._id !== payload), ///Removes the post from the array
loading: false,
};
component.js:
{!auth.loading && user === auth.user._id && (
<button
onClick={() => deletePost(_id)}
type='button'
className='btn btn-danger'
>
<i className='fas fa-delete-left'></i>
</button>
)}
I've tried debugging, and it seems that the deletePost() action doesn't even start running when I click the button.
I found out that this code actually deletes, but in action.js file when I dispatch the DELETE_POST type, it then runs the catch immedietaly. The problem is in the try{dispatch()} function, as what I want to log before it actually logs, but what I want to log after doesn't.
Edit: it says that dispatch is not a function for every dispatch in action.js file...
Related
I managed to update a boolean value from the database, but I want to get the value now in the interface, so the user will see exactly the value from the database.
I did this using redux, so here it's my action:
export const getSettings = () => async (dispatch) => {
dispatch({ type: CHANGE_SETTINGS_REQUEST, payload: {} });
try {
const { data } = await Axios.get(
"http://localhost:3030/v1/lootbox/getSettings"
);
dispatch({ type: CHANGE_SETTINGS_SUCCESS, payload: data });
localStorage.setItem("changeSettingsInfo", JSON.stringify(data));
} catch (error) {
dispatch({
type: CHANGE_SETTINGS_FAIL,
payload:
error.response && error.response.data.message
? error.response.data.message
: error.message,
});
}
};
In the component I have the following:
import { useEffect, useState } from "react";
import { useDispatch, useSelector } from "react-redux";
import { changeSettings, getSettings } from "../../actions/adminActions";
import MessageBox from "../../components/ErrorBox/MessageBox";
import LoadingBox from "../../components/Spinner/LoadingBox";
export default function LootboxSettings() {
const getSetting = useSelector((state) => state.getSetting);
const { changeSettingsInfo, loading, error } = getSetting;
const [checked, setChecked] = useState(changeSettingsInfo?.isVisible);
const dispatch = useDispatch();
const handleCheck = () => {
setChecked(!checked);
};
const submitHandler = (e) => {
e.preventDefault();
dispatch(changeSettings(checked));
};
useEffect(() => {
dispatch(getSettings());
}, []);
return (
<>
<div className='login'>Lootbox Settings</div>
{changeSettingsInfo && (
<form className='swapping-currency-checkbox' onSubmit={submitHandler}>
{loading && <LoadingBox></LoadingBox>}
{error && <MessageBox variant='danger'>{error}</MessageBox>}
<label>Is lootbox visible?</label>
<input
type='checkbox'
className='swapping-currency-check'
id='location'
name='location'
checked={checked}
onChange={handleCheck}
value='checkbox'
/>
<div>
<button className='buttonAccount swapping-btn' type='submit'>
Save
</button>
</div>
</form>
)}
</>
);
}
So if I console.log changeSettingsInfo, I'm getting undefined and then the boolean value from the database(that's why I used the ? optional parameter), if i delete the optional I get an error, because it is undefined, here are the logs:
Something I'm doing wrong, but I don't know what, maybe on the useEffect. What can i do?
Try this to reflect change:
useEffect(() => {
setChecked(changeSettingsInfo?.isVisible);
}, [changeSettingsInfo]);
Explaination:
The UI is binded by a local state variable which is initialized when a component mounts.
The dispatch triggers a change on the store, whose values are passed as props to the functional component. Causing a re-render on the UI.
In your case your console still prints undefined for state variable checked because quite frankly on re-rendering of the component you are not setting the value for checked
So in order to do that, create a specialized useEffect that triggers whenever the value of your variable changeSettingsInfo changes.
I'm having a little problem trying to do this:
Basically, I want to show another div if any error exists. This error comes from the payload to the next request.
But when I make the request for that (to have the error) and when I try to grab this error from the Redux state I'm having the problem that I put in the title 'Error: Rendered fewer hooks than expected. This may be caused by an accidental early return statement'
This is the function with problem:
Note: If the error doesn't not exist I can render very well the component.
Even when I have the error, if I reload the page the error "Error: Rendered fewer hooks than expected." disappears and is showing me correctly the div that I want "You don't have the correct access".
const Users: FC = () => {
const { error } = useSelector((state: State) => state.FleetUsers);
return (
<>
<Layout title={t('Fleet_users.table.My_users')} />
<LayoutFleet />
{error ? (
<div className="col-md-6 offset-md-3 mt-3">
<h2>You dont have the correct access</h2>
</div>
) : (
<div className="table-container">
<h2>You have the correct access</h2>
</div>
</div>
)}
</>
);
};
Code behavior:
I'm using Redux state so basically if I have the "error" is in the memory and I grab it directly using useSelector.
const { error } = useSelector((state: State) => state.FleetUsers);
This is the request :
export const fetchGroups = (
token: string,
): ThunkAction<void, State, null, FluxStandardAction> => {
return async (dispatch) => {
dispatch(FetchAllGroupsRequest());
try {
const response = await axios({
method: 'GET',
url: `${API}/users/organization-user-group`,
headers: {
Authorization: `Bearer ${token}`,
},
});
await response.data;
dispatch(FetchAllGroupsSuccess(response.data));
} catch (err) {
console.log(err);
dispatch(FetchAllGroupsFailure(err));
}
};
};
This is the reducer:
case FLEET_FETCH_ALL_GROUPS_REQUEST:
return {
...state,
loading: true,
};
case FLEET_FETCH_ALL_GROUPS_SUCCESS:
return {
...state,
loading: false,
groups: action.payload,
};
case FLEET_FETCH_ALL_GROUPS_FAILURE:
return {
...state,
loading: false,
error: action.payload.message,
};
If someone knows how to fix this React problem I will appreciate it a lot.
I am trying hard to remove an array. But it is not work. After delete my array length and data remain same.
This is my frontend react web page
renderAdmin = (id) => {
if(this.props.auth.user.userType==='normal') return(<td></td>);
return (
<React.Fragment>
<td>
<button onClick={() => this.props.changeStatus(id)}
className="ui button" >Change Status</button>
</td>
<td>
<button onClick={() => this.props.deleteUser(id)}
className="ui button" >Delete User</button>
</td>
</React.Fragment>
);
}
Here i am taking id of my array.
Now this is my redux action creator
export const deleteUser = (id) => (dispatch,getState) => {
const user = JSON.parse(localStorage.getItem('user'));
axios.delete(`/api/users/remove/${id}`, {
headers: {
'Authorization': `${user.token}`
}
}).then((res) => {
console.log(res.data);
dispatch({
type: 'DELETE_USER',
payload: res.data
})
dispatch({
type: 'CREATE_ERROR',
payload: 'User Delete'
})
})
.catch((error) => {
console.error(error)
})
}
Here first i remove it from backend server. That works fine. Then i send res.data to my reducer payload. DELETE_USER not working well. but CREATE_ERROR works fine.
This is my reducer
export default function (state = [] , action) {
// console.log(action)
switch(action.type) {
case 'ALL_USER' : return action.payload;
case 'EDIT_USER' :
return state.map(user=>{
if(user._id === action.payload._id){
return action.payload;
}
else {
return user;
}
});
case 'DELETE_USER' :
return state.filter(user=>
user !== action.payload
)
default : return state;
}
}
After pressing delete button, data remove from server but redux store always remain same. Please help me.
Thank You.
It might be due to the objects not being truly equal try filtering on the id instead:
return state.filter(user=>
user._id !== action.payload._id
)
Or check for deep equality.
cart.js
const Cart = () => {
const cartListing = useSelector(state => state.cartList);
const {cart, loading, error} = cartListing;
const dispatch = useDispatch();
const removeFromCartHandler = (id) => {
dispatch(removeFromCart(id))
}; // =======>>>>> This function needs to dispatch on onclick event but it fire on first time load and does not fire on click
useEffect(() => {
dispatch(cartList());
return () => {
}
},[]);
return(
<button type="button" className="button" onClick={removeFromCartHandler()} >Delete </button>
action.js
const cartList = () => async (dispatch) => {
try {
dispatch({ type: CART_LIST_REQUEST });
const { data } = await axios.get('http://localhost:3001/cart');
dispatch({ type: CART_LIST_SUCCESS, payload: data.data.cartRecords });
}
catch (error) {
dispatch({ type: CART_LIST_FAIL, payload: error.message });
}
};
const removeFromCart = (id) => async (dispatch) => {
try {
dispatch({ type: CART_REMOVE_ITEM, payload: id });
const { data } = await axios.delete(`http://localhost:3001/cart/${id}`);
dispatch({ type: CART_REMOVE_SUCCESS, payload: data });
}
catch (error) {
dispatch({type: CART_REMOVE_FAIL, payload: error.message})
}
};
export { cartList, removeFromCart };
I don't know why removeFromCartHandler() is firing on first time loading, not on click. I want that removeFromCartHandler() this function fire on onclick event not on the first-time load as I have also not added in useEffect hook cartList is fine dispatching on first time but removeFromCartHandler() function need to dispatch on onClick event. I think I am missing some concept of hooks here. I am new to react-redux and stucked here. Any help will be appricated
your onClick handler is wrong . Do it like this
onClick={() => removeFromCartHandler()}
It's being called because you are calling it - placing () after removeFromCartHandler invokes the function.
onClick={removeFromCartHandler()}
You need to add more code for better context but what you have to do is something like this
onClick={(e) => removeFromCartHandler(e, item.id)}
your main function expects to get id you have to pass it over somehow.
I presume that you render cart items or do something similar, anyhow with this implementation proper id will not be passed.
So I make an API call to server to get the currentUser,
useEffect(() => {
loadUser()
},[])
Since behaviour of React is like render first run lifecycle methods second, at first, my selector for user returns null which what I expect.
However I still got user is null error, so this is my code =>
const isAuthenticated = useSelector(state => state.auth.isAuthenticated)
const user = useSelector(state => state.auth.user)
const authLinks = (
<nav className="auth-navbar">
<div className="auth-navbar__dropdown">
<button type="button" className="dropdown-btn" onClick={dropdown}><img src={profilephoto}></img></button>
<div className="dropdown-menu">
<Link to={`u/${user.username}`} className="dropdown-link">Profile</Link>
<Link to="/settings" className="dropdown-link">Settings</Link>
<Link to="/" onClick={onClickHandler} className="dropdown-link">Logout</Link>
</div>
</div>
</nav>
)
if (user) {
return (
<header className="header">
<Link to="/" className="logo" >
<img src={logo} alt="logo"/>
</Link>
{isAuthenticated ? authLinks : guestLinks}
</header>
)
} else {
return <p>loading..</p>
}
Questions like this have been asked before on stackoverflow but solutions are similar to mine and it still doesn't work. Please help me.
P.S: the default of user is null in the reducer.
EDIT: The action creator to load user =>
export const loadUser = () => (dispatch) => {
dispatch({ type: USER_LOADING })
const config = {
withCredentials: true
}
axios.get('http://127.0.0.1:8000/auth/user/', config)
.then(res => {
dispatch({
type: USER_LOADED,
payload: res.data
})
}).catch(err => {
console.log(err)
dispatch({
type: AUTH_ERROR
})
})
}
Normally user loads without error =>
reducer =>
const initialState = {
isAuthenticated: false,
isLoading: false,
user: null,
}
export default function(state=initialState, action){
switch(action.type){
case USER_LOADING:
return {
...state,
isLoading: true
}
case USER_LOADED:
return {
...state,
isAuthenticated: true,
isLoading: false,
user: action.payload
}
I think you need to make authLinks a function which executes and returns the relevant JSX only when user exists. As it is, the variable will try to access the property user.username before it has been initialised.
const authLinks = () => (
...
);
And then call it in the return.
{isAuthenticated ? authLinks() : guestLinks}