I would like to understand why when the store is changed, the value in the useSelector is not updated.
I have two screens: screen A and screen B.
Screen A contains a FlatList which receives data from the redux.
Screen B contains a button that adds or removes items from the redux.
I am on Screen A -> I go to Screen B -> I remove an item from the redux -> I return to Screen A -> FlastList still contains the deleted item -> I go to Screen B again and return -> FlatList is updated fine.
Yet, if I view the console.log, the redux state is updated, but the template is not.
I don't know if I'm misunderstanding the concept or not, but I assume that the data should be propagated directly to the sheet as well.
import {useSelector} from 'react-redux';
export default function ScreenA() {
const timelines = useSelector((state: RootState) => {
// Once a removal or addition occurs, it's up-to-date right away
console.log(subscribedTimelines(state)); //->Returns the updated array
return subscribedTimelines(state);
});
return (
<View style={{flex: 1}}>
{/*The timelines variable still contains the old number of items, even if the state is updated.*/}
<FlatList
data={timelines}
keyExtractor={item => item.id}
renderItem={({item}) => (
<TouchableWithoutFeedback>
<View>
<TimelineListItem timeline={item} />
</View>
</TouchableWithoutFeedback>
)}
/>
</View>
);
}
Can someone please explain to me why this is not the case? Thank you!
Related
I'm trying to scroll sync my mapped components in react.
I have tried couple scroll sync libraries but I don't think they work on mapped components.(I would appreciate it if someone could help me out using scroll sync libraries. I have tried placing scrollSyncNode / scrollSyncPane inside the map function wrapping the component but it doesn't work.)
The below are two functions which I made where ref1 and ref2 scroll at the same time. I have tested the function with two independent components(not mapped) and they work fine.
I'm not completely sure if its possible but I want all the mapped components to equally share ref2.
the onScroll event will trigger the function.
I would really appreciate it if someone could give me a thorough guide to how I could scroll sync an independent component with the mapped components.
below is my parent component's code where both the independent component and mapped components are located:
const refOne = useRef();
const refTwo = useRef();
const handleScrollFirst = scroll => {
refTwo.current.scrollLeft = scroll.target.scrollLeft;
};
const handleScrollSecond = scroll => {
refOne.current.scrollLeft = scroll.target.scrollLeft;
};
return (
<ContentsWrapper>
<ThisIsRefOneDiv
style={{
width: '5vw',
overflowX: 'scroll',
}}
onScroll={handleScrollFirst}
ref={div1}
>
testtesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttest
</ThisIsRefOneDiv>
{data.map((group, idx) => (
<Card key={idx} color={group.color}>
<Objective data={group} />
{state && (
<Content>
<GanttData
data={group}
**onScroll={handleScrollSecond}
ref={refTwo}
/>
</Content>
)}
</Card>
))}
</ContentsWrapper>
);
}
API:
import {
createApi,
fetchBaseQuery,
} from '#reduxjs/toolkit/query/react'
import { RootState } from 'store'
export interface FeedType {
id: string
title: string
imgUrl: string
}
export const feedsApi = createApi({
reducerPath: 'feeds',
tagTypes: ['Feeds'],
baseQuery: fetchBaseQuery({
baseUrl: 'http://localhost:5000',
}),
endpoints: (build) => ({
getFeedsMore: build.query<FeedType[], void>({
async queryFn(arg, queryApi, extraOptions, baseQuery) {
const state = queryApi.getState() as RootState
const selector = feedsApi.endpoints.getFeedsMore.select() as (
state: any
) => any
const result = selector(state) as { data: FeedType[] } | undefined
const oldData = (result?.data ?? []) as FeedType[]
const { data } = await baseQuery({
url: 'feeds?_page=' + Math.round(oldData.length / 10 + 1),
})
return { data: [...oldData, ...(data as FeedType[])] }
},
}),
}),
})
export const {
useGetFeedsMoreQuery,
} = feedsApi
Component:
import FeedItem from 'components/FeedItem'
import React from 'react'
import Masonry from 'react-masonry-css'
import { useGetFeedsMoreQuery } from 'services/feeds'
interface FeedsMorePageProps {}
const FeedsMorePage: React.FunctionComponent<FeedsMorePageProps> = () => {
const { isLoading, data: feeds, refetch } = useGetFeedsMoreQuery()
return (
<>
{isLoading ? (
'loading'
) : (
<>
<Masonry
breakpointCols={{
default: 3,
1100: 2,
700: 1,
}}
className="my-masonry-grid"
columnClassName="my-masonry-grid_column"
>
{feeds &&
feeds.map((feed) => <FeedItem key={feed.id} feed={feed} />)}
</Masonry>
<button className="btn btn-info" onClick={() => refetch()}>
Load More...
</button>
</>
)}
</>
)
}
export default FeedsMorePage
I know it is totally a mess, this is the only clumsy way I could make it run.
What is the best practice to this scenario?
It is common to use dispatch and getState in a Thunk-Action, but the most confusing part to me is in queryFn I have getState and endpoint.select methods, but I do not know how to type them in typescript.
feedsApi.endpoints.getFeedsMore.select()(state as RootState)
// this gives me a super long type incompatible complain
I can not use useSelector hook here neither, util I made out this ugly way...
Generally, no, that's not what you should do. Building one cache entry that large will mean that eventually you will run out of memory. It can never be collected, so it will just keep growing. But if the user scrolled down to item 9500, you really don't need to keep item 1000 in memory any more.
Especially when you are displaying all those elements in your DOM.
And if you are not displaying all those elements in the DOM, there is also no need to have all of them in the cache.
So, assume you use some kind of virtualization library like react-virtual.
That means you know you have theoretically 10000 items to display, but you only render what is in view and a bit to the front and a bit to the back.
Then keep your endpoint also to a window.
Have your endpoint fetch "parts", so if the user is looking at item 9500, you maybe have items 9500-9550 on the screen and you want to keep some more fetched to quickly display them - one page to the front and one to the back.
So now you use your query hook three times in your component: the current "page" (9500-9550), the last "page" (9450-9500) and the next "page" (9550-9600).
That way, stuff not in view can at some point be cache-collected if it was not in view long enough.
Another way of doing that would be to just render "page" components from a parent component - and each of those "page" components would request their "window of data", while a "get more" button would add another element to the "pages" array in the parent component.
But either way: you would not stitch all of that together in the cache, but keep it as separate cache entries - and then decide to access which of those to access in your component and how to stitch them together.
Generally, I can recommend to read up on this GitHub discussion where multiple people share their approaches to the topic.
I have a component that I restricted to have certain types of children using flow types. However, now I have several situations where it is convenient to wrap those components in other component that just returns one of the valid components but with some defaults attached. Even grouping some of those valid components using react fragments gives me weird errors that I am unable to debug.
Here is a piece of sample code (that you can run on flow playground):
//#flow
import React from 'react'
import type { Node, Element, ChildrenArray } from 'react'
type ItemType = Element<typeof ListItem | typeof ListHeader>
type Props = {
children: ChildrenArray<ItemType>,
}
function List({ children }: Props) {
return <div>{children}</div>
}
type ItemProps = {
children: Node,
onClick?: () => void,
}
function ListItem({ children, onClick }: ItemProps) {
return <div onClick={onClick}>{children}</div>
}
type HeaderProps = {
children: Node,
}
function ListHeader({ children }: HeaderProps) {
return <div>{children}</div>
}
const Row = ({ left, right }: { left: string, right: Node }): Node => {
return (
<ListItem>
{left}
<span>{right}</span>
</ListItem>
)
}
const x = () => (
<List>
<Row left="gender" right="${gender}" />
<Row left="birthDate" right="xxx" />
<Row left="Number" right="xxx" />
<Row left="sendEnrollmentLetter" right="stuff" />
</List>
)
What is the correct way of following such pattern?
On flow playground you will not see errors because, if you open the console you will see that it just blows up.
This is the concrete error I'm getting on my project:
Cannot create `List` element because in array element of property `children`:
Either property `children` is missing in object type [1] but exists in `HeaderProps` [2] in property `props`.
Or property `children` is missing in object type [1] but exists in `ItemProps` [3] in property `props`.
Running your code snippet on a project gave the following errors for the child components inside x
Cannot create `Parent` element because Could not decide which case to select, since case 1 [1] may work but if it doesn't case 2 [2] looks promising too. To fix add a type annotation to return [3].Flow(incompatible-type)
Assuming these are the errors you're getting, flow can't infer what the type of your components should be, so it wants you to manually annotate them. Adding return type of Node should do it
const Wrapper = (): Node => (
<>
<Foo disabled />
<Bar />
</>
);
const Defaults = (): Node => <Foo disabled={false} />;
const x = (
<Parent>
<Wrapper />
<Wrapper />
<Defaults />
</Parent>
);
Though as a side note I'd strongly recommend you move to using import * as React from 'react'; so you can use React.Node instead. Otherwise Node from react will conflict with usage of html Node in the future.
I'm trying to create a flip card effect where if I click on a card it flips over. However, if I then click on another card, I'd like the original card to flip back. This means having some sort of global toggle on all of the cards but one that gets overridden by the local toggle. I'm not sure of the most React-y way to do this - I've tried implementing it with the useState hook at both levels, but I'm not having much luck.
I'm using styled-components, with the 'flipped' prop determining the Y-transform.
Here's the flip card component, and you can see what I've tried so far:
const PortfolioItem = props => {
const [flipped, setFlipped] = useState(false)
return (
<PortfolioItemStyles onClick={() => setFlipped(!flipped)}>
// what I'm trying to say here is, if the individual card's 'flipped' is set to true,
use that, otherwise use props.flipped which will be set to false
<PortfolioItemInnerStyle flipped={flipped ? flipped : props.flipped}>
<PortfolioItemFront >
{props.image}
<PortfolioImageCover className="img-cover" />
<PortfolioItemHeader>{props.title}</PortfolioItemHeader>
</PortfolioItemFront>
<PortfolioItemBack>
<h1>Hello there</h1>
</PortfolioItemBack>
</PortfolioItemInnerStyle>
</PortfolioItemStyles>
)
}
function PortfolioStyles() {
const [ allFlipped, setAllFlipped ] = useState(false);
return (
<PortfolioContainer>
{portfolioItems.map(item => {
return <PortfolioItem image={item.image} flipped={allFlipped} title={item.title} onClick={() => setAllFlipped(false)} />
})}
</PortfolioContainer>
)
}
The logic I'm using is clearly faulty, but I was wondering what would be the 'best practice' way of doing this? In vanilla JS you'd use a single event handler and use event.target on it to make sure you were isolating the element, but I'm not sure how to handle this in React. Any help would be much appreciated.
I would personally manage the state only on the container component. Let's say you will store an index of the flipped card instead of a true/false status. The onClick would then change the current index and the flipped is computed by checking index === currentIndex. Something like this:
const PortfolioItem = props => {
return (
<PortfolioItemStyles>
<PortfolioItemInnerStyle flipped={props.flipped}>
<PortfolioItemFront >
{props.image}
<PortfolioImageCover className="img-cover" />
<PortfolioItemHeader>{props.title}</PortfolioItemHeader>
</PortfolioItemFront>
<PortfolioItemBack>
<h1>Hello there</h1>
</PortfolioItemBack>
</PortfolioItemInnerStyle>
</PortfolioItemStyles>
)
}
function PortfolioStyles() {
const [ currentFlippedIndex, setCurrentFlippedIndex ] = useState(-1);
return (
<PortfolioContainer>
{portfolioItems.map((item, index) => {
return <PortfolioItem image={item.image} flipped={index === currentFlippedIndex} title={item.title} onClick={() => setCurrentFlippedIndex(index)} />
})}
</PortfolioContainer>
)
}
I have made a login button which will jump to main-ui. I don't want people to see the back button at top-left position of navigator's default style.
It seems that NavigatorIOS has no suitable API to use. Can you give me some idea?
You'll have to use Navigator. I recommend using it in conjunction with React Native Navbar.
With Navbar, you can pass in the right and left button components... Or just make them an empty view, if they shouldn't be visible. The property is leftButton and rightButton.
The navigator example on the React docs should get you started:
<Navigator
initialRoute={{name: 'My First Scene', index: 0}}
renderScene={(route, navigator) =>
<MySceneComponent
name={route.name}
onForward={() => {
var nextIndex = route.index + 1;
navigator.push({
name: 'Scene ' + nextIndex,
index: nextIndex,
});
}}
onBack={() => {
if (route.index > 0) {
navigator.pop();
}
}}
/>
}
/>
Now in the definition of MySceneComponent you would include displaying NavBar:
const MySceneComponent = (props) => (
<View style={{ flex: 1, }}>
<NavigationBar
title={titleConfig}
rightButton={BUTTON_OR_NOT}
{...props} />
{props.children}
</View>
);
Of course you will want to abstract your navigation bits perhaps into a component which displays the navigation bar around it as I have shown above with the display of {children}. You will further want to pass the route and navigation information into Navbar so it can display the page information and make it so that clicking on the back button calls navigator.pop(), which is what I did by passing on props via {...props}.