Using a placeholder image in react native without state manipulation - react-native

So the part below is a code snippet. These images are rendered in a flatlist.
<Image
resizeMode='cover'
style={styles.itemImage}
source={(product && !_.isEmpty(imageSource) && imageSource.url) ? { uri: imageSource.url } : noProductImage}
/>
Now some URLs provided will be present but it may be a broken link, so it'll pass the condition. Now I solved this by adding states for each image source but if the list has 100 items then we need to maintain 100 different states that can potentially be the same. So if there some way to solve this without states. Like maybe check the URL in the conditional statement.

You can use one array in state for all images.
this.state = {
images : [
{id: 0, url: '...'},
{id: 1, url: '...'},
...
]
}
Maybe it's not the best solution, but in such case you will work only with one state.

Related

What does getHandlerId() do and how to use it?

Some of the react-dnd examples use a getHandlerId() method.
For example in the simple example of a sortable list, the Card.tsx function:
Collects a handlerId from the monitor object within the useDrop method
collect(monitor) {
return {
handlerId: monitor.getHandlerId(),
}
},
Returns that as an element of the "collected props"
const [{ handlerId }, drop] = useDrop<
Uses it to initialize an HTML attribute named data-handler-id
<div ref={ref} style={{ ...style, opacity }} data-handler-id={handlerId}>
What is this Id and why is it used?
What uses the data-handler-id attribute?
I'd expect to see getHandlerId() described in the API documentation as a method of the DropTargetMonitor (but it isn't).
I didn't dive deep into it but for me this information was enough to continue using it:
If you remove this data-handler-id, everything continue working but with some issues (item sometimes flickers, it doesn't go to another place as smoothly as it does with data-handler-id)
Here is an open issue https://github.com/react-dnd/react-dnd/issues/2621 about low performance, and this comment suggests to use handler id: https://github.com/react-dnd/react-dnd/issues/2621#issuecomment-847316022
As you can see in code https://github.com/react-dnd/react-dnd/search?q=handlerId&type=code, handler id is using for proper definition of drop item so it seems better to use it even if you don't have a lot of elements.

Object reactivity of complex object

I have an issue with complex object reactivity.
I've read everything I can on stack to find a way to solve it, but nothing works. I've looked at object reactvity and array caveats on vuejs, but not working either.
So I'm asking some help please.
Let me explain the project:
I have 2 columns :
- on the left side, I CRUD my content
- on the right side, I display the results
I have my object, and I'm adding new elements on its "blocks" property (text, images, etc...)
[
{
"uid": 1573224607087,
"animation": "animationName",
"background": {
"bckColor": "#ff55ee",
...
},
"blocks": []
}
]
On click event, I add a new element via this method. Everything is ok, I can CRUD a block.
addBloc(el) {
if (el.type == "text") {
const datasA = {
type: "text",
uid: Date.now(),
slideId: this.pagination.currentPage,
content: el.content,
css: {
color: "#373737",
...
},
...
};
this.slides[this.pagination.currentPage].blocks.push(datasA);
this.$bus.$emit("newElement", datasA);
}
To modify the order of my elements on the display side, I added a drag and drop module to move my block on my DOM tree. Smooth dnd
The problem is, when I drang&drop my element, my object is updated correctly, but the DOM isn't. The dragged element goes back to its initial position.
What is strange, when I try to modify my block (the one I dragged), it modifies the other one.
I'me adding a small video, so you can see what's happening.
Small animation to show you what's going on
I add some more explainations.
I use event bus to communicate between my components, and the right side is using its own object!
I don't know how I can solve this issue.
Tell me if you need more information.
Thank you all !
EDIT 1 :
I added an id to each block to see what happens when I start Drag&Drop. ==> blocks are moving correctly. The problem is not coming from the method onDrop() but from my nested components if I understand well. They don't update. I'm going to search for this new issue.
I've added a new gif to show what's going on.
This is the nested structure
TheSidebar.vue => top container
<Container
:data-index="i"
#drop="onDrop(i,$event)"
:get-child-payload="itemIndex => getChildPayload(i, itemIndex)"
lock-axis="y"
>
<Draggable
v-show="pagination.currentPage === i"
v-for="(input, index) in slides[i].blocks"
:key="index.uid"
:id="'slideBlocksContainer'+index"
class="item"
>
blockId #{{input.uid}}
<AppContainer
v-if="input.type == 'text'"
:blocType="input.type"
:placeholder="input.content"
:id="index"
:slideId="i"
></AppContainer>
</Draggable>
</Container>
Then I have my AppContainer.vue file, which is a top level. In this I have the specific elements of each input type
And I have AppElement.vue file, which is common elements, I can use everywhere
Something like this
TheSidebar
--AppContainer
----AppElement
Know I don't know yet, how to force vue to update AppContainer.vue and AppElement.vue
EDIT 2 :
As suggested in this article I've changed the key of the component and now , when I drag and drop my elements, they stay where they are dropped.
What I see also, is that the AppElement inputs, are related to their own AppContainer. So everything is ok now, but I don't know if it is best practices.
The issue appears to be that the Smooth dnd library you are using is not updating the array of blocks that you are passing to it, it is likely making a copy of the array internally. So when you change the position of the blocks by dragging and dropping, you are not changing your blocks array, just the internal copy.
Looking at the Smooth dnd documentation, if you wanted to access the modified array you could try using the drag-end event handler:
onDragEnd (dragResult) {
const { isSource, payload, willAcceptDrop } = dragResult
}

How do I render a message when there are no filtered results in React?

I have a search bar that updates the state based on user input. Components that don't match the keywords I've set up will get filtered out and will not render to the screen. How do I display a message when there are no matches in the search bar?
An example can be found on CodeSandbox here:
https://codesandbox.io/embed/search-bar-test-odxw8
I am using an array of objects to store the data. I'm using the map function to separate the data into individual components, and the filter function to perform a re-render as the user inputs a word.
I tried using logic that says "if the array length is 0, display the message". The problem with that is the array is apparently always the same length and isn't connected to the amount of items being rendered to the screen.
I also tried using logic that says "if the inputted word doesn't match the object's keywords, display the message". This doesn't throw an error, but it also does not work as expected. The message remains visible at all times.
My state:
this.state = {
AnimalList,
AnimalFilter: ""
};
An example of my conditional logic:
{/* This is where I'm trying to place the conditional logic */}
{/* For example, typing "pig" should make this appear */}
{this.state.AnimalFilter!== AnimalList.keywords && (
<h2>No Results</h2>
)}
The filter and map methods:
<div className="container">
{AnimalList.filter(animal =>
animal.keywords
.toLocaleLowerCase()
.includes(this.state.AnimalFilter.toLocaleLowerCase())
).map(({ object1, object2, object3 }, index) => (
<AnimalCard
key={index}
object1={object1}
object2={object2}
object3={object3}
/>
))}
</div>
The Data:
export default [
{
object1: "horse",
object2: "goat",
object3: "panda",
keywords: "horse goat panda"
},
{
object1: "horse",
object2: "cat",
object3: "cow",
keywords: "horse cat cow"
},
{
...
},
...
];
I expect for the message to display to the screen when a user inputs a word that "doesn't exist". As of now, the message will either display the entire time or not at all.

Prepending data to a FlatList always shows the first child

this is our FlatList, say hello:
<FlatList
data={this.state.dates}
...
/>
we feed it with the following dates:
this.state = {
dates: [
'21/06/2019',
'22/06/2019',
'23/06/2019',
]
};
then when the visible date changes (onViewableItemsChanged), if we end up to the first item (21/06/2019), we prepend data, so that the new state becomes:
dates: [
'18/06/2019',
'19/06/2019',
'20/06/2019',
'21/06/2019',
'22/06/2019',
'23/06/2019',
]
The Problem:
right after we prepend the data, instead of STILL seeing 21/06/2019 (which was the date when the prepend took place) we now see 19/06/2019.
That's because below the hood, 21/06/2019 used to be index 0, but after the prepend, index 0 corresponds to 19/06/2019.
What we want:
I'm trying to make it so that the day remains the same after prepending data.
Please don't tell me to use scrollToPosition because that's a hack really, not the solution.
Is there any good solution to that problem?
Thank you
There is an undocumented prop maintainVisibleContentPosition on ScrollView that do what you want, but unfortunately it's not working on android
I found another workaround by keep latest y offset with onScroll and also save content height before and after adding new items with onContentSizeChange and calculate the difference of content height, and set new y offset to the latest y offset + content height difference!
I've opened an issue on github also, but there is not any complete solution yet
Thanks to sgtpepper43 for undocumented iOS solution
that maintainVisibleContentPosition does not work if you prepend data while you are at a scroll position of 0 (all the way to the top). It does work if you are prepending while not a the top. Even scroll Y of 1 would be enough.
check this
Unfortunately this still an issue even in React Native 0.63.3 and has not been solved.
Leaving this here, since it took me a while to get a working solution/ workaround to this problem that doesn't leave a bad taste in my mouth: prepopulate the array with empty data, and then use the scrollToIndex method.
this.state = {
dates: [
'',
'',
'',
'21/06/2019',
'22/06/2019',
'23/06/2019',
]
};
And then:
<FlatList
data={this.state.dates}
ref={flatListRef}
getItemLayout={(data, index) => (
{length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
)}
...
/>
inside your componentDidMount:
const startingIndex = 4
flatListRef.current.scrollToIndex({index: startingIndex, animated: false, viewPosition: 0})
There's a library: https://github.com/GetStream/react-native-bidirectional-infinite-scroll that allows you to both preprend or append data and preserve scroll position
It's basically an extension over the FlatList and supports all the props available for a plain FlatList
From their example usage tutorial: https://dev.to/vishalnarkhede/react-native-how-to-build-bidirectional-infinite-scroll-32ph#%F0%9F%96%A5-tutorial-chat-ui-with-bidirectional-infinite-scroll
It looks like it would be enough to just prepend that data to the top exactly like you're trying to do

Refer to a key in map to add content. React native

I am doing an app that gets information about a sports game from a provider. They provide goals and assists in two different objects, looks something like this:
incidents: {
1: {
id: 1,
type: 'goal'
},
2: {
id: 2
type: 'assist'
referto: 1
}
As you can see in the object above, the object with id 2 is an assist which refers to object with id 1.
So I want to map this object and return a <View> with the data, and if type = assist, I want it to append to the View which the id refers to.
Below is a mix of jQuery and React, but I hope you understand.
Object.map(incident => {
if (incident.type === 'assist') {
incident.referto.append( //refer to the View with key = incident.referto
<View><Text>I am an assist to the goal above</Text></View>
);
}
)};
How can I do something like this?
Thanks in advance!
EDIT:
I want to add "assist" view component inside the "goal" view component. I hope that will make it a bit clearer, sorry.
Best way of going at it is to use a conditional render.
{
incident.type === 'assist' &&
<View>
<Text>I am an assist to the goal above</Text>
</View>
}
By doing so you add the views only when incident type is assist, otherwise they are non-existent.
While not advised, another way of going at it would be to abuse React.createElement(component, props, ...children). However, such a solution is likely not what you want, and probably what you wish to achieve can be achieved using JSX.