Call component function that inside a page from another page - javascript

I'm trying to understand how I can trigger OnSwipe from inside another JS file called CardFooter.js containing a button with OnClick triggering OnSwipe from the previous JS file called CardItem.js. They both are called inside Card.js.
I'm learning Nextjs with Reactjs components. Any insight would greatly be appreciated
CardFooter.js
import { CardItem } from '../components/CardItem'
const CardFooter = () => {
return (
<Button onClick={() => CardItem.onSwipe('left')}></Button>;
<Button onClick={() => CardItem.onSwipe('right')}></Button>;
)
}
export default CardFooter
CardItem.js
import { useState, useContext, createContext } from 'react'
import { Context } from '../context/Context'
import TinderCard from 'react-tinder-card'
import { test } from '../components/CardFooter';
export const CardItem = ({ card }) => {
const { handleRightSwipe, handleLeftSwipe, currentAccount } = useContext(Context)
const onSwipe = dir => {
if (dir === 'right') {
handleRightSwipe(card, currentAccount)
}
if (dir === 'left') {
handleLeftSwipe(card, currentAccount)
}
}
return (
<TinderCard
preventSwipe={['up', 'down']}
onSwipe={onSwipe}
>
<div style={{ backgroundImage: `url('${card.imageUrl}')` }}>
<div>
{card.name}
</div>
</div>
</TinderCard>
)
}
export default CardItem
Card.js
import { useContext } from 'react'
import { Context } from '../context/Context'
import CardFooter from './CardFooter'
import CardItem from './CardItem'
const Card = () => {
const { cardsData } = useContext(Context)
return (
<div className={style.wrapper}>
<div className={style.cardMain}>
<div className={style.swipesContainer}>
{cardsData.map((card, index) => (
<CardItem card={card} key={index} />
))}
</div>
</div>
<CardFooter />
</div>
)
}
export default Card

In React, you can't pass props up from child components into parent components. This means that you wouldn't be able to pass the handleSwipe function up to <CardFooter>.
But it doesn't look like you'd need to do that anyways, since you have the handleRightSwipe and handleLeftSwipe functions within your Context, and all the handleSwipe function is really doing is calling those functions.
So, with this in mind, there are two solutions:
In your footer, import the context and use it, then when the user clicks on the right button, call handleRightSwipe, and handleLeftSwipe for the left button. (this is the naive solution)
Define handleSwipe within the parent <Card> component, then pass it as a prop to both of the components. This way, you only need to define the function once. (ideal solution)

Related

React nextJS function references it self and pulls data without an import

I bought this template and am trying to understand which page getLayout in this code"{getLayout(<Component {...pageProps} />)}" goes to on initial load. I'm guessing it's a global variable somewhere, but I can't find it using the definitions. I'm trying to under the next.js documentation, but I'm having issues. If anyone has a good tutorial for this I'll happily take it.
import type { AppProps } from 'next/app';
import { appWithTranslation } from 'next-i18next';
import { SessionProvider } from 'next-auth/react';
import '#/assets/css/main.css';
import 'react-toastify/dist/ReactToastify.css';
import { ToastContainer } from 'react-toastify';
import { ModalProvider } from '#/components/ui/modal/modal.context';
import ManagedModal from '#/components/ui/modal/managed-modal';
import ManagedDrawer from '#/components/ui/drawer/managed-drawer';
import DefaultSeo from '#/components/seo/default-seo';
import { SearchProvider } from '#/components/ui/search/search.context';
import PrivateRoute from '#/lib/private-route';
import { CartProvider } from '#/store/quick-cart/cart.context';
import SocialLogin from '#/components/auth/social-login';
import { NextPageWithLayout } from '#/types';
import QueryProvider from '#/framework/client/query-provider';
import { getDirection } from '#/lib/constants';
import { useRouter } from 'next/router';
type AppPropsWithLayout = AppProps & {
Component: NextPageWithLayout;
};
function CustomApp({
Component,
pageProps: { session, ...pageProps },
}: AppPropsWithLayout) {
// Use the layout defined at the page level, if available
const getLayout = Component.getLayout ?? ((page) => page);
const authenticationRequired = Component.authenticationRequired ?? false;
const { locale } = useRouter();
const dir = getDirection(locale);
return (
<div dir={dir}>
<SessionProvider session={session}>
<QueryProvider pageProps={pageProps}>
<SearchProvider>
<ModalProvider>
<CartProvider>
<>
<DefaultSeo />
{authenticationRequired ? (
<PrivateRoute>
{getLayout(<Component {...pageProps} />)}
</PrivateRoute>
) : (
getLayout(<Component {...pageProps} />)
)}
<ManagedModal />
<ManagedDrawer />
<ToastContainer autoClose={2000} theme="colored" />
<SocialLogin />
</>
</CartProvider>
</ModalProvider>
</SearchProvider>
</QueryProvider>
</SessionProvider>
</div>
);
}
export default appWithTranslation(CustomApp);
Basically, you can define a per component layout. In order to do so, when you are defining a component, you add a property named getLayout. Here's an example, for a better understanding.
// ...
const Component: React.FC = () => <div>
I have a custom layout
</div>
// Here we define a layout, let's imagine it's a component
// we have inside /layouts and we have previously imported it
Component.getLayout = (page: React.ReactElement) =>
<LayoutComponent>{page}</LayoutComponent>
Now, when a page is rendered (note that in a Next JS app, all pages are rendered as a children of what is inside _app.{js,jsx,ts,tsx}) your code checks if the prop getLayout has been defined or not. Then, it is basically calling such function, if exists, otherwise it renders the base component.

Is there a way to call different rtq hooks based on state or prop? Maybe by using skip?

I have a component in which i want to call different rtkquery hooks based on a condition. I am making a twitter clone and on the home page i want to call a getPostsList and on the profile page i want to call a getRetweetedPostsList but since the entire page is same apart from this i am using a single component. Now i want to call different hooks in the PostList component based on props? Is this possible? Maybe by using skip? Also is this against best practices?
index.tsx
import type { NextPage } from 'next'
import { useSession} from 'next-auth/react';
import SignUpLoginFullScreen from '../components/SignUpLoginFullScreen';
import LoadingScreen from '../components/LoadingScreen';
import PostsSection from '../components/Posts/PostsSection';
const Home: NextPage = () => {
const {data:session,status}=useSession();
return (
<>
{!session && status==='unauthenticated' &&
<SignUpLoginFullScreen/>
}
{!session && status==='loading' &&
<LoadingScreen/>
}
{session && status==='authenticated' &&
<PostsSection/>
}
</>
)
}
export default Home
PostSection.tsx
import React from 'react'
import { useSession} from 'next-auth/react';
import Sidebar from '../Sidebar';
import Search from '../Search';
import PostsList from '../Posts/PostsList';
import AddPostForm from '../Posts/AddPostForm';
import Modal from '../Modal';
import UsersList from '../UsersList';
const PostsSection=()=>{
const {data:session,status}=useSession();
return (
<>
<Modal>
<AddPostForm />
</Modal>
<div className='flex mx-32 gap-x-5'>
<Sidebar/>
<main className='mr-5 pt-8 flex-1 basis-[45%] border-x-2 border-stone-100 min-h-screen'>
<PostsList currUserId={session?.userId}/>
</main>
<div className='basis-[25%]'>
<Search/>
<UsersList currentUserId={session?.userId}/>
</div>
</div>
</>
)
}
export default PostsSection
PostList.tsx
import React from 'react'
import {useGetPostsQuery} from '../../services/apiSlice';
import LoadingSpinner from '../LoadingSpinner';
import Post from './Post';
interface PropsType{
currUserId:string|any
}
const mapPosts=(posts:any,currUserId:string)=>{
return posts?.map((post:any) => (
<Post key={post.id} currUserId={currUserId} {...post}/>
))
};
const PostsList:React.FC<PropsType>= ({currUserId}) => {
const {data:posts,isLoading,error,isError} = useGetPostsQuery(currUserId);
let content;
content=React.useMemo(()=>mapPosts(posts,currUserId), [posts]);
if(isLoading){
content=<LoadingSpinner/>
}
else if(isError){
let a:any=error
content=<p color='red'>{a?.message}</p>
}
else if(posts){
if(posts.length<=0){
console.log('aye')
content=<p color='black'>No tweets yet</p>;
return null;
}
}
return (
<section className="posts-list">
{content}
</section>
)
}
export default PostsList;
I want to call the PostList component from the profile page but with some props and based on that props i want to call a different hook to which i am calling for the index page.
Profile.tsx
import React from 'react';
import { useSession } from 'next-auth/react';
import LoadingScreen from '../components/LoadingScreen';
import Sidebar from '../components/Sidebar';
import Search from '../components/Search';
import PostsList from '../components/Posts/PostsList';
import AddPostForm from '../components/Posts/AddPostForm';
import Modal from '../components/Modal';
import UsersList from '../components/UsersList';
import SignUpLoginFullScreen from '../components/SignUpLoginFullScreen';
import PostsSection from '../components/Posts/PostsSection';
export default function Profile() {
const {data:session,status}=useSession();
return (
<>
{!session && status==='unauthenticated' &&
<SignUpLoginFullScreen/>
}
{!session && status==='loading' &&
<LoadingScreen/>
}
{session && status==='authenticated' &&
<PostsSection/>
}
</>
)
}
I would use different components to call different hooks, then pass the data to a reusable common component.

Why is useEffect being triggered without dependency change when working with modals?

I'm having trouble working with useEffect to fetch comments when using a modal. I have a PostMain component that is displayed inside a modal, as seen below. Inside this, there is a CommentsList child component that fetches comments left under the post from the server. I have created a custom hook to handle this, as seen below. The problem I'm facing is whenever I exit the modal, then reopen it, useEffect is triggered even though its dependencies (pageNumber, postId) haven't changed. A server request similar to the initial one is sent, with the same comments being added to the list, as seen in the screenshots below. Obviously, this is not ideal. So, what am I doing wrong? How do I fix this?
Fetch Comments Custom Hook
import { useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { fetchComments } from '../store/comments/actions';
function useFetchComments(pageNumber, commentsPerRequest = 5, postId) {
const { error, hasMoreComments, isLoading, commentList } = useSelector(
({ comments }) => ({
error: comments.error,
hasMoreComments: comments.hasMoreComments,
isLoading: comments.isLoading,
commentList: comments.commentList,
})
);
const currentCommentListLength = commentList.length || 0;
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchComments(pageNumber, commentsPerRequest, currentCommentListLength, postId));
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [pageNumber, postId]);
return {
error,
hasMoreComments,
isLoading,
commentList,
};
}
export default useFetchComments;
Post Component
import React from 'react';
import { useSelector } from 'react-redux';
import { Image, Modal } from 'semantic-ui-react';
import CommentForm from '../../forms/comment';
import CommentList from '../../shared/comment-list';
function PostMain({ post }) {
const { isLoggedIn } = useSelector(({ auth }) => ({
isLoggedIn: auth.isLoggedIn,
}));
return (
<Modal size="tiny" trigger={<Image src={post.url} />}>
<Modal.Content>
<div>
<Image src={post.url} />
<CommentList postId={post._id} />
{isLoggedIn && (
<CommentForm postId={post._id} />
)}
</div>
</Modal.Content>
</Modal>
);
}
export default PostMain;
Comment List Component
import React, { useState } from 'react';
import { useFetchComments } from '../../../hooks';
function CommentList({ postId }) {
const COMMENTS_PER_REQUEST = 5;
const [pageNumber, setPageNumber] = useState(1);
const { error, isLoading, commentList, hasMoreComments } = useFetchComments(
pageNumber,
COMMENTS_PER_REQUEST,
postId
);
const handleFetchMoreComments = () => {
setPageNumber((previousNumber) => previousNumber + 1);
};
return (
<div>
<div>
{commentList.map((comment) => (
<div key={comment._id}>{comment.body}</div>
))}
{hasMoreComments && (
<p onClick={handleFetchMoreComments}>View More</p>
)}
</div>
{isLoading && <p>Loading...</p>}
{error && <p>{JSON.stringify(error)}</p>}
</div>
);
}
export default CommentList;
First instance of opening modal
Second instance of opening modal

Export function from React functional component and use it elsewhere

I want to export function from one of my functional component that is using hooks to another one. I want to prevent redundant code appearing in my components.
I have tried to create separate function.js file where I wanted to place some of my functions but useDispatch hook makes it impossible as it throws hell a lot of errors in every attempt to make it work.
I was searching for solution and trying some export statements in different combinations.
What I want to do is to export my toggleDrawer function from Layout component to other components and here's my code. I'm sure it's very easy and I'm missing something.
import React from 'react'
import { useSelector, useDispatch } from 'react-redux'
import Header from '../Header/header'
import DrawerItems from '../DrawerItems/drawerItems'
import { REDUCERS } from '../../Config/config'
import Container from '#material-ui/core/Container'
import Drawer from '#material-ui/core/Drawer'
import { makeStyles } from '#material-ui/core/styles'
const useDrawerStyles = makeStyles({
paper: {
width: '175px',
padding: '10px'
}
})
const Layout = props => {
const { isDrawerOpened } = useSelector(state => {
return {
...state.interface_reducer
}
})
const dispatch = useDispatch()
const drawerClasses = useDrawerStyles()
const toggleDrawer = (side, open) => event => {
if (event.type === 'keydown' && (event.key === 'Tab' || event.key === 'Shift')) {
return null
}
dispatch({
type: REDUCERS.TOGGLE_DRAWER,
payload: open
})
}
return (
<Container>
<React.Fragment>
<Header/>
<Drawer classes={{paper: drawerClasses.paper}} open={isDrawerOpened} onClose={toggleDrawer('left', false)} >
<DrawerItems/>
</Drawer>
{ props.children }
</React.Fragment>
</Container>
)
}
export default Layout
Define the function in another file. Or define it in that file and export it. Then you can import it in other files for other components.

How to get event from components to container in React Redux

I'm new to Redux.
I handled the basic Facebook Flux architecture very easily and made some nice app with it.
But I struggle very hard to get very simple Redux App to work.
My main concern is about containers and the way they catch events from components.
I have this very simple App :
CONTAINER
import { connect } from 'react-redux'
import {changevalue} from 'actions'
import App from 'components/App'
const mapStateToProps = (state) => {
return {
selector:state.value
}
}
const mapDispatchToProps = (dispatch) => {
return {
onClick: (e) => {
console.log(e)
dispatch(changeValue())
}
}
}
const AppContainer = connect(
mapStateToProps,
mapDispatchToProps
)(App)
export default AppContainer;
Component
import React, {Component} from 'react'
import Selector from 'components/Selector'
import Displayer from 'components/Displayer'
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick}/>
<Displayer />
</div>
)
export default App;
CHILD COMPONENT
import React, {Component} from 'react'
const Selector = ({onClick}) => (
<div onClick={onClick}>click me</div>
)
export default Selector;
onClick event does not reach the container's mapDispatchToProps.
I feel that if I get this work, I get a revelation, and finally get the Redux thing! ;)
Can anybody help me get this, please ? (The Redux doc is TOTALLY NOT helpfull...)
The problem is in the App component. In the onClick property of the Selector component, you're passing a function which returns the definition of a function, not the result.
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick}/> // here is the problem
<Displayer />
</div>
)
You should simply do this instead:
const App = (selector, onClick) => (
<div>
<Selector onClick={(e) => onClick(e)}/>
<Displayer />
</div>
)
Or even simpler:
const App = (selector, onClick) => (
<div>
<Selector onClick={onClick}/>
<Displayer />
</div>
)

Categories

Resources