cant access state, react redux - javascript

I have a page with notes for a specific user, the problem is when i'm trying to access the state of 'my notes' i get 'undefined' on console, in postman, everything works when the user login, and I can see the notes.
notes reducer
import {
NOTES_LIST_REQUEST,
NOTES_LIST_SUCCESS,
NOTES_LIST_FAIL,
} from '../types/noteTypes';
export const noteListReducer = (state = { notes: [] }, action) => {
switch (action.type) {
// whenever call the api its going to:
// request the notes
case NOTES_LIST_REQUEST:
return { loading: true };
// if true..
case NOTES_LIST_SUCCESS:
return { loading: false, notes: action.payload };
//if fail..
case NOTES_LIST_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
notes action
import {
NOTES_LIST_REQUEST,
NOTES_LIST_SUCCESS,
NOTES_LIST_FAIL
} from '../types/noteTypes';
import axios from 'axios';
export const listNotes = () => async (dispatch, getState) => {
try {
// will set the loading to true
dispatch({ type: NOTES_LIST_REQUEST });
// fetching the user info from the state
const{
userLogin: { userInfo },
} = getState();
//just like sending bearer token from postman to backend
const options = {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
};
const data = await axios('http://localhost:5000/api/notes', options);
console.log(data);
// const returnDataFromServer = await data.json()
// .then(dataa=>console.log(dataa))
// console.log(returnDataFromServer);
// if request is success dispatch this action and pass the data to the notes state inside the reducer
dispatch({
type: NOTES_LIST_SUCCESS,
payload: data.data
});
} catch (err) {
const message =
err.response && err.response.returnDataFromServer.message
? err.response.returnDataFromServer.message
: err.message;
// if fails fire this action and pass the message
dispatch({
type: NOTES_LIST_FAIL,
payload: message,
});
}
};
notes page
here I'm trying to access the state of 'my notes' but get undefined
import React, { useEffect } from 'react';
import { Link } from 'react-router-dom';
import MainPage from '../../component/MainPage';
import { Badge, Button, Card, Accordion, AccordionCollapse, AccordionButton } from 'react-bootstrap';
import { useDispatch, useSelector } from 'react-redux';
import { listNotes } from '../../redux/actions/notesAction';
import Loading from '../../component/Loading';
import ErrorMessage from '../../component/ErrorMessage';
export default function MyNotes() {
//take notes out from our state
const dispatch = useDispatch();
// name to the state
const noteList = useSelector(state => state.noteList);
console.log(noteList);
//destructing what we need from state
// const { loading, error, notes } = noteList;
const deleteHandler = (id) => {
if (window.confirm('Are You Sure?')) {
}
};
useEffect(() => {
dispatch(listNotes())
}, [dispatch])
// render the notes that come from the backend
return (
<MainPage title="welcome back avi vovgen...">
<Link to='createNewNot'>
<Button className="btn btn-info" style={{ marginLeft: 10, marginBottom: 10 }} size="lg" >
Create new Note
</Button>
</Link>
{/*
{error && <ErrorMessage variant="danger">{error}</ErrorMessage>}
{loading && <Loading />} */}
{/* {notes.map((note) => (
<Accordion key={note._id}>
<Card style={{ margin: 10 }}>
<Card.Header style={{ display: "flex" }}>
<span
style={{
color: "black",
textDecoration: "none",
flex: 1,
cursor: "pointer",
alignSelf: "center",
fontSize: 18
}}>
<AccordionButton as={Card.Text} variant="link" >
{note.title}
</AccordionButton>
</span>
<div>
<Button href={`/note/${note._id}`}>Edit</Button>
<Button variant="danger" className="mx-2" onClick={() => deleteHandler(note._id)}>Delete</Button>
</div>
</Card.Header>
<AccordionCollapse eventKey="">
<Card.Body>
<h4>
<Badge className="btn btn-success">
category-{note.category}
</Badge>
</h4>
<blockquote className="blockquote mb-0">
<p>
{note.content}
</p>
<footer className="blockquote-footer">
Created on-date
</footer>
</blockquote>
</Card.Body>
</AccordionCollapse>
</Card>
</Accordion>
)) */}
}
</MainPage >
)
}

Related

Why is always the first changed?

I can update a Note but if I update it it updates the first note which is in myPosts section.
I really don´t understand. If I click on a Note which I create then it goes to the site "/notesEdit" so and I can see there the current forumName and forumDescription but if I change it, it doesn´t change the current selected note instead the first in List.
Here are the Notes which the user sees which he has created :
import React, { useEffect } from "react";
import { Button, Accordion } from "react-bootstrap";
import { Link } from "react-router-dom";
import MainScreen from "../components/MainScreen";
import ReactMarkdown from "react-markdown";
import { useDispatch, useSelector } from "react-redux";
import { deleteNoteAction, listForumUser } from "../../redux/forum/noteActions";
function NotesMe({ history, search }) {
const dispatch = useDispatch();
const userLogin = useSelector((state) => state.userLogin);
const { userInfo } = userLogin;
const noteList = useSelector((state) => state.noteList);
const { notes } = noteList;
console.log(noteList);
const deleteHandler = (_id) => {
if (window.confirm("Are you sure?")) {
dispatch(deleteNoteAction(_id));
dispatch(listForumUser());
}
};
useEffect(() => {
dispatch(listForumUser());
}, [dispatch]);
return (
<MainScreen title={` ${userInfo.userName}´s Forum..`}>
<Link to="createForum">
<Button style={{ marginLeft: 10, marginBottom: 6 }} size="lg">
Create New Forum
</Button>
</Link>
{notes &&
notes.map((forum) => (
<Accordion key={forum._id} defaultActiveKey="0">
<Accordion.Item style={{ margin: 10 }} key={forum._id}>
<Accordion.Header style={{ display: "flex" }}>
<span
style={{
color: "black",
textDecoration: "none",
flex: 1,
cursor: "pointer",
alignSelf: "center",
fontSize: 18,
}}
>
{forum.forumName}
</span>
<div>
<Link
to={{
pathname: "/notesEdit",
query: { forum },
}}
>
<Button id="EditButton">Edit</Button>
</Link>
<Button
variant="danger"
className="mx-2"
onClick={() => deleteHandler(forum._id)}
>
Delete
</Button>
</div>
</Accordion.Header>
<Accordion.Body>
<blockquote className="blockquote mb-0">
<ReactMarkdown>{forum.forumDescription}</ReactMarkdown>
<footer className="blockquote-footer">
Created on{" "}
<cite title="Source Title">
{/* {forum.published_on && forum.published_on.substring(0, 300)} */}
{/* {forum.user.userName && forum.user.userName} */}
</cite>
</footer>
</blockquote>
</Accordion.Body>
</Accordion.Item>
</Accordion>
))}
</MainScreen>
);
}
export default NotesMe;
and this is the site where the user can change it :
import React, { useState, useEffect } from "react";
import { Form, Button, Row, Col } from "react-bootstrap";
import { useLocation } from "react-router";
import MainScreen from "../components/MainScreen";
import { useDispatch, useSelector } from "react-redux";
import { updateNoteAction } from "../../redux/forum/noteActions";
import "./NotesEdit.css";
const NotesEdit = ({ history }) => {
const [forumName, setforumName] = useState("");
const [forumDescription, setforumDescription] = useState("");
const { query } = useLocation();
const dispatch = useDispatch();
const userLogin = useSelector((state) => state.userLogin);
const { userInfo } = userLogin;
const noteList = useSelector((state) => state.noteList);
const { notes } = noteList;
useEffect(() => {
if (query) {
setforumName(query.forum.forumName);
setforumDescription(query.forum.forumDescription);
}
}, [query]);
const submitHandler = (e) => {
e.preventDefault();
dispatch(updateNoteAction({ forumName, forumDescription }));
};
return (
<MainScreen title="EDIT MyNote">
<div id="ForumEdit">
<Row className="ForumContainer">
<Col md={6}>
<Form onSubmit={submitHandler}>
<Form.Group controlId="forumName">
<Form.Label>forumName</Form.Label>
<Form.Control
id="forumNameInput"
type="text"
placeholder="Enter forumName"
value={forumName}
onChange={(e) => setforumName(e.target.value)}
></Form.Control>
</Form.Group>
<Form.Group controlId="forumDescription">
<Form.Label>forumDescription</Form.Label>
<Form.Control
id="forumDescriptionInput"
type="text"
placeholder="Enter forumDescription"
value={forumDescription}
onChange={(e) => setforumDescription(e.target.value)}
></Form.Control>
</Form.Group>
<Button
id="EditButton"
type="submit"
varient="primary"
onClick={submitHandler}
>
Update
</Button>
</Form>
</Col>
<Col
style={{
display: "flex",
alignItems: "center",
justifyContent: "center",
}}
></Col>
</Row>
</div>
</MainScreen>
);
};
export default NotesEdit;
export const updateNoteAction = (forum) => async (dispatch, getState) => {
try {
dispatch({
type: NOTES_UPDATE_REQUEST,
});
const {
userLogin: { userInfo },
} = getState();
const url = "http://localhost:8080/forum/";
const config = {
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${userInfo.token}`,
},
};
const { data } = await axios.put(url, forum, config);
dispatch({
type: NOTES_UPDATE_SUCCESS,
payload: data,
});
} catch (error) {
const message =
error.response && error.response.data.message
? error.response.data.message
: error.message;
dispatch({
type: NOTES_UPDATE_FAIL,
payload: message,
});
}
};
const forumSchema = ({
forumName: {
type: String,
required: true,
},
forumDescription: {
type: String,
required: true,
},
user: {
type: Schema.Types.ObjectId,
ref: 'user',
},
published_on: {
type: String,
default: moment().format("LLL")
},
});

TypeError: Cannot read property 'data' of undefined for shopping cart functionality

I keep getting this error: TypeError: Cannot read property 'data' of undefined, when there is no data being passed to my shopping cart page. How can I fix this error? Ideally, I would just like the page to display: "This cart is empty". I tried adding a conditional statement above the UserCardBlock, but it did not change anything. Thank you
import React, { useState } from 'react'
import { useDispatch } from 'react-redux';
import {
removeCartItem,
onSuccessBuy
} from '../../../_actions/user_actions';
import UserCardBlock from './Sections/UserCardBlock';
import { Result, Empty, Button } from 'antd';
import Paypal from '../../utils/Paypal';
function CartPage(props) {
const dispatch = useDispatch();
console.log(props)
const [Total, setTotal] = useState(props.location.state.data.price)
const [ShowTotal, setShowTotal] = useState(true)
const [ShowSuccess, setShowSuccess] = useState(false)
const removeFromCart = (productId) => {
dispatch(removeCartItem(productId))
}
const transactionSuccess = (data) => {
dispatch(onSuccessBuy({
cartDetail: props.user.cartDetail,
paymentData: data
}))
.then(response => {
setShowSuccess(true)
setShowTotal(false)
}
)
}
const transactionError = () => {
console.log('Paypal error')
}
const transactionCanceled = () => {
console.log('Transaction canceled')
}
const propductList = (data) =>{
console.log(data)
setTotal(data)
}
return (
<div style={{ width: '85%', margin: '3rem auto' }}>
<h1>My Cart</h1>
<div>
<UserCardBlock
productData={props.location.state.data}
removeItem={removeFromCart}
productList={data => propductList(data)}
/>
{ShowTotal ? (
<div style={{ marginTop: "3rem" }}>
<h2>Total amount: ${Total * 15} </h2>
</div>
) : ShowSuccess ? (
<Result status="success" title="Successfully Purchased Items" />
) : (
<div
style={{
width: "100%",
display: "flex",
flexDirection: "column",
justifyContent: "center",
}}
>
<br />
<Empty description={false} />
<p>No Items In The Cart</p>
</div>
)}
</div>
{/* Paypal Button */}
{ShowTotal &&
<Paypal
toPay={Total}
onSuccess={transactionSuccess}
transactionError={transactionError}
transactionCanceled={transactionCanceled}
/>
}
</div>
)
}
export default CartPage
Seems like your component is dependent on location state.
const [Total, setTotal] = useState(props.location.state.data.price)
and
<UserCardBlock
productData={props.location.state.data}
Try using optional chaining and nullish coalescing
const [Total, setTotal] = useState(props.location?.state?.data?.price ?? 0)
<UserCardBlock
productData={props.location.state?.data ?? []}
It seems like you are using redux so i will suggest you to use redux store instead of location state.

Passing props to modal passes every object

I'm not 100% sure what's going on here. I've got a display component that displays a bunch of cards, using a map based on my database - On the card is an edit button that pops a modal up, passing props over to the edit form.. Here's kinda how it looks:
import React, { useState } from 'react'
import { useQuery, useMutation } from '#apollo/client'
import { GET_ALL_PROJECTS, REMOVE_PROJECT } from '../helpers/queries'
import { makeStyles } from '#material-ui/core/styles'
import DeleteIcon from '#material-ui/icons/Delete'
import EditIcon from '#material-ui/icons/Edit'
import AddForm from './AddForm'
import EditForm from './EditForm'
import AlertMessage from '../Alerts/AlertMessage'
import { Grid, Typography, Card, CardActionArea, CardActions, CardContent, CardMedia, Button, Modal, Backdrop, Fade } from '#material-ui/core'
const useStyles = makeStyles((theme) => ({
modal: {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
},
paper: {
backgroundColor: theme.palette.background.paper,
border: '2px solid #000',
boxShadow: theme.shadows[5],
padding: theme.spacing(2, 4, 3),
},
}));
const DisplayProjects = () => {
const styles = useStyles()
const [deleteItem] = useMutation(REMOVE_PROJECT)
const { loading, error, data } = useQuery(GET_ALL_PROJECTS)
const [status, setStatusBase] = useState('')
const [resultMessage, setResultMessage] = useState('')
const [addOpen, setAddOpen] = useState(false)
const [editOpen, setEditOpen] = useState(false)
const onDelete = (id, e) => {
e.preventDefault()
deleteItem({
variables: { id },
refetchQueries: [{ query: GET_ALL_PROJECTS }]
}).then(
res => handleSuccess(res),
err => handleError(err)
)
}
// Handles Result of the Delete Operation
const handleSuccess = (res) => {
console.log(res.data.deleteProject.proj_name)
// console.log('success!');
setResultMessage(res.data.deleteProject.proj_name)
setStatusBase({
msg: `Successfully Deleted ${resultMessage}`,
key: Math.random()
})
}
const handleError = (err) => {
console.log('error')
}
//Handles the Modal for Add Project
const handleAddOpen = () => {
setAddOpen(true);
};
const handleAddClose = () => {
setAddOpen(false);
};
//Handles the Modal for Edit Project
const handleEditOpen = () => {
setEditOpen(true);
};
const handleEditClose = () => {
setEditOpen(false);
};
if (loading) return '...Loading'
if (error) return `Error: ${error.message}`
return (
<div>
<div style={{ marginTop: 20, padding: 30 }}>
<Grid container spacing={8} justify='center' alignItems='center'>
{data.projects.map(p => {
return (
<Grid item key={p._id}>
<Card >
<CardActionArea>
<div style={{ display: 'flex', justifyContent: 'center' }}>
<CardMedia
style={{ width: 400, height: 100, paddingTop: 10, }}
component='img'
alt='Project Image'
height='140'
image={require('../../images/html-css-javascript-lg.jpg')}
/>
</div>
<CardContent >
<Typography gutterBottom variant='h5' component="h2">
{p.proj_name}
</Typography>
<Typography component='p'>
{p.description}
</Typography>
</CardContent>
</CardActionArea>
<CardActions>
<Button>
<DeleteIcon onClick={e => onDelete(p._id, e)} />
</Button>
<Button onClick={handleEditOpen}>
<Modal
open={editOpen}
onClose={handleEditClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={editOpen}>
<div className={styles.paper}>
<EditForm
id={p._id}
close={handleEditClose}
name={p.proj_name}
desc={p.description}
gh={p.gh_link}
live={p.live_link}
img={p.image_url}
/>
</div>
</Fade>
</Modal>
<EditIcon />
</Button>
</CardActions>
</Card>
{ status ? <AlertMessage key={status.key} message={status.msg} /> : null}
</Grid>
)
}
)}
</Grid>
<Button type='button' onClick={handleAddOpen}>Add Project</Button>
<Modal
open={addOpen}
onClose={handleAddClose}
closeAfterTransition
BackdropComponent={Backdrop}
className={styles.modal}
>
<Fade in={addOpen}>
<div className={styles.paper}>
<AddForm close={handleAddClose} />
</div>
</Fade>
</Modal>
</div>
</div >
)
}
export default DisplayProjects
And here's the form. I've destructured out the props into variables and placed them into a state object called details, so they can be overwritten and submitted to the database..
import React, { useState } from 'react'
import { useParams } from 'react-router-dom'
import { useMutation, useQuery } from '#apollo/client'
import { EDIT_PROJECT, GET_ALL_PROJECTS, GET_PROJECT_BY_ID} from '../helpers/queries'
const AddForm = (props) => {
const params = useParams()
const id = params.toString()
// console.log(id);
const [editProjectItem] = useMutation(EDIT_PROJECT)
const {loading, data, error} = useQuery(GET_PROJECT_BY_ID, {
variables: {
id
},
})
const [details, setDetails] = useState({})
if (loading) return '...Loading';
if (error) return <p>ERROR: {error.message}</p>;
if (!data) return <p>Not found</p>;
setDetails(data.projectById)
console.log(data.projectById)
const submitForm = e => {
e.preventDefault()
try {
editProjectItem({
variables: { id, proj_name, description, gh_link, live_link, image_url},
refetchQueries: [{query: GET_ALL_PROJECTS}]
})
}
catch (err) {
console.log('You Goofed')
}
// setDetails({
// proj_name: '',
// description: '',
// gh_link: '',
// live_link: '',
// image_url: ''
// })
props.close()
}
const changeDetails = (e) => {
setDetails({
...details,
[e.target.name]: e.target.value
})
}
const {_id, proj_name, description, gh_link, live_link, image_url} = details
return (
<div key = {_id}>
<h2>Edit {proj_name}</h2>
<form onSubmit = {submitForm} >
<label>
Project Name:
<input
name = 'proj_name'
value = {proj_name}
onChange = {changeDetails}
/>
</label>
<label>Description</label>
<input
name = 'description'
value = {description}
onChange = {changeDetails}
/>
<label>GitHub Link</label>
<input
name = 'gh_link'
value = {gh_link}
onChange = {changeDetails}
/>
<label>Live Link</label>
<input
name = 'live_link'
value = {live_link}
onChange = {changeDetails}
/>
<label>Preview Image</label>
<input
name = 'image_url'
value = {image_url}
onChange = {changeDetails}
/>
<button type = 'submit'>Submit</button>
</form>
</div>
)
}
export default AddForm
The problem I'm running into, is that when I access the modal, the props are sent from literally EVERY Object, instead of the one, and displays the data for the last record instead of the one I want to edit You can see what happens here (I logged props.id in order to test) https://imgur.com/a/pcEKl89
What did I miss? (Disclaimer: I am still a student, and learning the craft.. be gentle on my code please)
EDIT: I just realized that I didn't indicate that this is the final form of the EditForm component. I haven't added the logic in to make the updates yet, I just wanted to get the data showing properly first.
EDIT2: I made some changes to how the ID is passed over, I was already using React-Router, so I went ahead and made a route to /edit/:id and then using useParams(), I got the ID that way. It seems to be working, however now I'm getting a Too many re-renders message. Updated the AddForm code above to reflect the changes..
I figured out the re-render issue.. it was as simple as dropping the setDetails function into a useEffect Hook:
useEffect(()=> {
if(data){
setDetails(data.projectById)
}
},[data])

On home i get that error: The action 'SET_PARAMS' with the payload {"params":{"company":"Marciello Resto 2"}} was not handled by any navigator

I am now using React native navigation 5 and in my "Home component I have a setParams which works but I get that error:
"The action 'SET_PARAMS' with the payload {"params":{"company":"Marciello Resto 2"}} was not handled by any navigator."
I am on Android Emulator.
Thank you all for your help!
//App.js
import * as React from "react"
import { View, Text, ActivityIndicator, StyleSheet } from 'react-native'
import { NavigationContainer } from '#react-navigation/native'
import { createStackNavigator } from '#react-navigation/stack'
import { createDrawerNavigator } from '#react-navigation/drawer'
import EtiquettesPictureContext from './Components/Contexts/EtiquettesPictureContext'
import RootStack from './Navigation/Navigation'
import Home from './Components/Layouts/Home'
import Etiquettes from './Components/Layouts/Etiquettes'
import TemperaturesFridges from './Components/Layouts/TemperaturesFridges'
import BonsLivraisons from './Components/Layouts/BonsLivraisons'
import EtiquettesTakePicture from "./Components/EtiquettesTakePicture"
import BonsLivraisonsTakePicture from "./Components/BonsLivraisonsTakePicture"
import DlcAlertTakePicture from "./Components/DlcAlertTakePicture"
import Notifications from "./Components/Layouts/Notifications"
import Settings from "./Components/Layouts/Settings"
import Welcome from "./Components/Layouts/Welcome"
import Login from "./Components/Layouts/Login"
import Signup from "./Components/Layouts/Signup"
import InitLogin from "./Components/Layouts/InitLogin"
import MesEtiquettes from "./Components/Layouts/MesEtiquettes"
import WhatRole from "./Components/WhatRole"
const AuthContext = React.createContext()
const Stack = createStackNavigator()
const Drawer = createDrawerNavigator()
export default function App({ navigation }) {
const [etiquettesPictureData, blPictureData, setEtiquettesPictureData, setBlPictureData] = React.useState("")
updateInitialData = (data) => {
setEtiquettesPictureData(data)
}
updateBlData = (data) => {
blPictureData(data)
}
const [state, dispatch] = React.useReducer(
(prevState, action) => {
switch (action.type) {
case 'RESTORE_TOKEN':
return {
...prevState,
userToken: action.isLogged,
isLoading: false,
};
case 'SIGN_IN':
return {
...prevState,
isSignout: false,
userToken: action.isLogged,
};
case 'SIGN_OUT':
return {
...prevState,
isSignout: true,
userToken: null,
};
}
},
{
isLoading: true,
isSignout: false,
userToken: null,
}
);
React.useEffect(() => {
// Fetch the token from storage then navigate to our appropriate place
const bootstrapAsync = async () => {
let isLogged;
try {
isLogged = await InitLogin.initLogin()
} catch (e) {
// Restoring token failed
}
// After restoring token, we may need to validate it in production apps
// This will switch to the App screen or Auth screen and this loading
// screen will be unmounted and thrown away.
dispatch({ type: 'RESTORE_TOKEN', isLogged: isLogged.logged });
};
bootstrapAsync();
}, []);
const authContext = React.useMemo(
() => ({
signIn: async data => {
// In a production app, we need to send some data (usually username, password) to server and get a token
// We will also need to handle errors if sign in failed
// After getting token, we need to persist the token using AsyncStorage
// In the example, we'll use a dummy token
dispatch({ type: 'SIGN_IN', isLogged: 'dummy-auth-token' });
},
signOut: () => dispatch({ type: 'SIGN_OUT' }),
signUp: async data => {
// In a production app, we need to send user data to server and get a token
// We will also need to handle errors if sign up failed
// After getting token, we need to persist the token using `AsyncStorage`
// In the example, we'll use a dummy token
dispatch({ type: 'SIGN_IN', isLogged: 'dummy-auth-token' });
},
}),
[]
)
if (state.isLoading) {
// We haven't finished checking for the token yet
return (
<View style={[styles.container, styles.horizontal]}>
)
}
function RootStackScreen() {
return (
<Stack.Navigator>
{state.userToken == false ? (
<>
<Stack.Screen name="Welcome" component={Welcome} />
<Stack.Screen name="Login" component={Login} />
<Stack.Screen name="Signup" component={Signup} />
</>
) : (
<>
<Stack.Screen name="Home" component={Home} options={({ route }) => ({ title: route.params ? route.params.company : "" })} />
<Stack.Screen name="Etiquettes" component={Etiquettes} />
<Stack.Screen name="TemperaturesFridges" component={TemperaturesFridges} />
</>
)}
</Stack.Navigator>
)
}
function DrawerScreen() {
return (
<Drawer.Navigator>
<Drawer.Screen name="TemperaturesFridges" component={TemperaturesFridges} />
</Drawer.Navigator>
)
}
return (
<EtiquettesPictureContext.Provider
value={{
etiquette: etiquettesPictureData,
Bl: blPictureData,
updateInitialData: updateInitialData,
updateBlData: updateBlData
}}>
<AuthContext.Provider value={authContext}>
<Drawer.Navigator>
<Drawer.Screen name="Home" component={RootStackScreen} />
</Drawer.Navigator>
</AuthContext.Provider>
</EtiquettesPictureContext.Provider>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
},
horizontal: {
flexDirection: 'row',
justifyContent: 'space-around',
padding: 10
}
})
//Home.jsimport * as React from 'react';
import mainStyleSheet from "../../Themes/main.js"
import { MaterialHeaderButtons, Item} from '../HeaderButtons';
import Ionicons from 'react-native-vector-icons/FontAwesome5';
import Company from '../Entities/Company';
import Notification from '../Entities/Notification'
import {
SafeAreaView,
ScrollView,
View,
Text,
TouchableHighlight
} from 'react-native';
export default function Home({ navigation}) {
React.useEffect(() => {
Company.getCompany().then(company => {
navigation.setParams({ company })
})
this.didBlurSubscription = navigation.addListener(
'didFocus',
() => {
Notification.countNotifications()
.then(resp => {
navigation.setParams({ notificationsCount: resp.length })
})
}
)
// returned function will be called on component unmount
return () => {
this.didBlurSubscription()
}
}, [])
const { navigate} = navigation;
return (
<TouchableHighlight style={[mainStyleSheet.buttonHomeContainer, { marginTop: 0 }]} onPress={() => navigate('Etiquettes')} >
<Ionicons name="tag" style={{ marginLeft: 20 }} color="white" size={30} />
Etiquettes produits
<TouchableHighlight style={mainStyleSheet.buttonHomeContainer} onPress={() => navigate('TemperaturesFridges')}>
<Ionicons name="thermometer-quarter" style={{ marginLeft: 20 }} color="white" size={30} />
Relevés de température
<TouchableHighlight style={mainStyleSheet.buttonHomeContainer} onPress={() => navigate('BonsLivraisons')}>
<Ionicons name="file-invoice" style={{ marginLeft: 20 }} color="white" size={30} />
Bons de livraisons
<TouchableHighlight style={mainStyleSheet.buttonHomeContainer} onPress={() => navigate('DownloadableDocuments')}>
<Ionicons name="file-image" style={{ marginLeft: 20 }} color="white" size={30} />
Documents à télécharger
)
}
I believe this issue has to do with calling navigation.setParams when the component is unmounting. According to the react navigation docs [0],
The setParams method lets us update the params (route.params) of the current screen
If the current screen is already unmounting when setParams gets called, then it would make sense that the navigator would not be able to handle it. Rework your logic so you are not setting params on blur.
[0] https://reactnavigation.org/docs/navigation-prop/#setparams

Clicking like icons increases the like count of other components

Upon the click of a single like, it is increasing the number of likes for both separate components. What is causing both like numbers to increase, and how can I code it to where only one like number increases upon clicking a like?
I have also include the console in the picture below where I have console logged the logic in my reducer. You can find the code for the reducer further below the picture.
Reducer code
import { GET_GOALS, GOAL_ERROR, UPDATE_LIKES } from "../actions/types";
const initialState = {
goals: [],
goal: null,
loading: true,
error: {}
};
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case GET_GOALS:
return {
...state,
goals: payload,
loading: false
};
case GOAL_ERROR:
return {
...state,
error: payload,
loading: false
};
case UPDATE_LIKES:
return {
...state,
goals: state.goals.map(goal =>
console.log("goal id", goal._id) === console.log("payload id", payload.goalId) ? { ...goal, likes: payload.likes } : goal
),
loading: false
};
default:
return state;
}
}
Action code
import axios from "axios";
import { GET_GOALS, GOAL_ERROR, UPDATE_LIKES } from "./types";
// Get goals
export const getGoals = () => async dispatch => {
try {
const res = await axios.get("/api/goal/goalfeed");
dispatch({
type: GET_GOALS,
payload: res.data
});
} catch (error) {
dispatch({
type: GOAL_ERROR,
payload: { msg: error.response }
});
}
};
// Add like
export const addLike = goalId => async dispatch => {
try {
const res = await axios.put(`/api/goal/like/${goalId}`);
dispatch({
type: UPDATE_LIKES,
payload: { goalId, likes: res.data }
});
} catch (error) {
dispatch({
type: GOAL_ERROR,
payload: { msg: error.response }
});
}
};
// Remove like
export const removeLike = goalId => async dispatch => {
try {
const res = await axios.put(`/api/goal/unlike/${goalId}`);
dispatch({
type: UPDATE_LIKES,
payload: { goalId, likes: res.data }
});
} catch (error) {
dispatch({
type: GOAL_ERROR,
payload: { msg: error.response }
});
}
};
Goals component code
import React, { useEffect } from "react";
import Moment from "react-moment";
import PropTypes from "prop-types";
import { Link } from "react-router-dom";
import { connect } from "react-redux";
import { addLike, removeLike } from "../../actions/goal";
import { getGoals } from "../../actions/goal";
import Spinner from "../layout/Spinner";
import Navbar from "../dashboard/Navbar";
import ThumbUpAltIcon from "#material-ui/icons/ThumbUpAlt";
import ThumbDownAltIcon from "#material-ui/icons/ThumbDownAlt";
import ChatIcon from "#material-ui/icons/Chat";
import DeleteIcon from "#material-ui/icons/Delete";
import DoneIcon from "#material-ui/icons/Done";
import {
Typography,
Container,
CssBaseline,
makeStyles,
Grid,
Avatar,
Paper,
Button
} from "#material-ui/core";
const useStyles = makeStyles(theme => ({
paper: {
height: "auto",
marginBottom: theme.spacing(3)
},
actionButtons: {
marginTop: "3vh"
},
profileHeader: {
textAlign: "center",
marginBottom: 20
},
avatar: {
width: theme.spacing(7),
height: theme.spacing(7)
}
}));
const Goals = ({
getGoals,
auth,
addLike,
removeLike,
goal: { goals, user, loading }
}) => {
useEffect(() => {
getGoals();
}, [getGoals]);
const classes = useStyles();
return loading ? (
<>
<Navbar />
<Container component="main" maxWidth="xs">
<CssBaseline />
<div className={classes.paper}>
<Spinner />
</div>
</Container>
</>
) : (
<>
<CssBaseline />
<Navbar />
<main>
<Container>
<Typography variant="h2" className={classes.profileHeader}>
Goals
</Typography>
{/* parent grid */}
<Grid container spacing={4}>
{goals.map(singleGoal => (
<Grid
className={classes.paper}
key={singleGoal._id}
spacing={1}
container
item
direction="row"
alignItems="center"
component={Paper}
>
<Grid
item
container
direction="column"
justify="center"
alignItems="center"
xs={3}
>
<Avatar className={classes.avatar} src={singleGoal.avatar} />
<Typography variant="caption">
{singleGoal.first_name} {singleGoal.last_name}
</Typography>
<Typography variant="caption" className={classes.postedOn}>
Posted on{" "}
<Moment format="MM/DD/YYYY">{singleGoal.date}</Moment>
</Typography>
</Grid>
<Grid container item direction="column" xs={9}>
<Typography variant="body1">{singleGoal.text}</Typography>
<Grid item className={classes.actionButtons}>
<Button size="small" onClick={e => addLike(singleGoal._id)}>
<ThumbUpAltIcon />
</Button>
<Typography variant="caption">
{singleGoal.likes.length}
</Typography>
<Button
size="small"
onClick={e => removeLike(singleGoal._id)}
>
<ThumbDownAltIcon />
</Button>
<Button href={`/goal/${singleGoal._id}`} size="small">
<ChatIcon />
</Button>
{!auth.loading && singleGoal.user === auth.user._id && (
<Button size="small">
<DoneIcon />
</Button>
)}
{!auth.loading && singleGoal.user === auth.user._id && (
<Button size="small">
<DeleteIcon />
</Button>
)}
</Grid>
</Grid>
</Grid>
))}
</Grid>
</Container>
</main>
</>
);
};
Goals.propTypes = {
getGoals: PropTypes.func.isRequired,
goal: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
goal: state.goal,
auth: state.auth
});
export default connect(mapStateToProps, { getGoals, addLike, removeLike })(
Goals
);
There exists a flaw in your conditional test.
state.goals.map(goal =>
console.log("goal id", goal._id) === console.log("payload id", payload.goalId) // What is this? it will always evaluate to true
? { ...goal, likes: payload.likes }
: goal
)
console.log('EQUAL?', console.log() === console.log()); // true
console.log('EQUAL?', console.log(3) === console.log(3)); // true
console.log('EQUAL?', console.log(3) === console.log('three')); // true
console.log('EQUAL?', console.log('apple') === console.log({})); // true
console.log('EQUAL?', console.log(42) === console.log(-42)); // true
The function console.log is a void return, i.e. undefined, so you are comparing undefined === undefined, which is always true.
console.log(undefined === undefined); // true
You are spreading in the new 'likes' value to every goal object.
Try instead:
state.goals.map(
goal => goal._id === payload.goalId
? { ...goal, likes: payload.likes }
: goal
)

Categories

Resources