How to create a Crossword board in React Native - react-native

I'm trying to create a crossword game in react native. I'm having trouble starting off with the gameboard. I think I'm going to have the crosswords stored in an object like
{
across: {
1: {
question: "test",
answer: "test",
position:(0,0),
length: 4,
}
}
down:{}
}
Would it make sense to create a matrix of 0 for black squares 1 for white squares and 2 for word starting squares. Then use a flat list to build out the matrix visually?
Any help or advise on another way to do it would be appreciated.
Cheers,
I've. tried. using flat lists but the indexing becomes very complicated and I'm hoping there is a better way.

I made one of those React Pathfinding visualizers and basically just had an array I kept track of thru state for if it was filled or not. Map/ForEach that grid and plop down what you would have as another component shall we say Node passing whatever information is needed as props.
This example may not be the best, due to it being React and not React Native (small difference really)... and there is a lot to this that doesn't apply to your scenario but I think it shows what I mentioned in the beginning.
<div className="grid">
{grid.map((row, rowId) => {
return (
<div key={rowId}>
{row.map((node, nodeId) => {
const { row, col, isFinish, isStart, isWall } = node;
return (
<Node
key={nodeId}
row={row}
col={col}
isStart={isStart}
isFinish={isFinish}
isWall={isWall}
mouseIsPressed={this.state.mouseIsPressed}
onMouseDown={(row, col) => this.handleMouseDown(row, col)}
onMouseEnter={(row, col) =>
this.handleMouseEnter(row, col)
}
onMouseUp={() => this.handleMouseUp()}
></Node>
);
})}
</div>
);
})}
</div>

I'd map the crossword data to an array of fields. There are three types of fields in crosswords: question block, fillable block and dead block.
Algorithmically, there are countless options. One would be to first convert every question to its blocks and then convert all of these to a flat array of blocks, combined.
Extra tip: consider using an array of questions instead of an object indexed by numbers. These indexes don't matter anyway.

Related

vue does not recover from me specifying a non existing location for v-model

When I have a textarea like
<textarea v-model="foo.abc.text"></textarea>
and either foo or foo.abc does not exist yet then
vue removes either parts of the DOM or is giving me a blank page.
It does never recover.
That alone is annoying, regardless of if I am using a debug version of vue or not.
If I try to use an approach that I have been advised to use earlier like
<textarea v-model="foo?.abc?.text"></textarea>
then I am still out of luck, I presume that I get a "rvalue" using those question marks and what I need rather is a variable location.
How do I, with as little trickery as possible, allow v-model to exist later on even if it doesnt exist now (late binding)?
Just shape your data accordingly and initialize it with empty values:
data(){
return {
foo: {
abc: {
text: ''
}
}
}
}
You can later populate it e.g. with the result of api call, but it's still better to initialize data properly
I would suggest going the :value + #input way. It allow more control over the input model, and does not require hiding it.
<textarea :value="!!foo && foo.abc.text" #input="(val) => !!foo && (foo.abc.text = val)" />
You can even hook in a validator:
<textarea
:value="!!foo && foo.abc.text"
#input="(val) => !!foo && (foo.abc.text = val)"
:rules="v => !v && 'The object has not been initialised'"
/>
I found a solution I can live with and then I got a comment in the same direction:
Conditionally showing the textarea.
v-if seems to do it but it falls under the "trickery" category, I think (angularjs would be more relaxed).
<textarea v-if="foo!=null" v-model="foo.abc"></textarea>
The symptom to hiding components if something is not all correct is not the best part of vue.js. Better show them and color them red.

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
}

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.

Sectioning data in a ListView

Say you have a list of People incoming from your API.
[{content: 'John'},
{content: 'Tim'},
{content: 'Harry J. Epstein'}]
And you're looking to put people who are first-name-basis friends (John and Tim) under a section 'Friends' and people who are not (Harry J. Epstein) under 'Contacts'.
Tapping a friend selects them with a blue highlight, but tapping a 'contact' selects them with a red highlight.
Would the proper approach be to take the incoming data from the API, add a type: 'Friend', ... or type: 'Contact', ... around it, and section based on that type with separate a FriendItem and ContactItem class so I can split the highlighting function?
I've got a bunch of just basic ListView code that does this exact approach, but I'm basically looking for the easy way out, like Angulars ng-repeat equivalent.
So what's the React Native version of
var friends = api.getFriends()
var contacts = api.getContacts()
<div ng-repeat="friend in friends" ng-click="highlightFriend()"> ... </div>
<div ng-repeat="contact in contacts" ng-click="highlightContact()"> ... </div>
I'm struggling to understand how to split it. Do I need a FriendsPage, FriendsItem, and ContactsItem? Or put everything into one array in FriendsPage and use a FriendsItem that checks if it's a friend or contact and adds a function separately?
I feel like I'm slightly lost coming from MVC. I've got Redux running too, if there's an easy way using that.
Here is a nice example on how you can create section-dependent rows: https://github.com/spoeck/ListViewExample
The idea is basically to create the data blob properly, which is a bit tricky, and then in your renderRow callback, check the sectionID parameter:
_renderRow(rowData: any, sectionID: any, rowID: number) {
if (sectionID === this.data[0].section) {
return <MyFriends />
} else if (sectionID === this.data[1].section) {
return <MyContacts />
}else{
// ...
}
}
why don't you try SectionList
Use the new FlatList or SectionList component instead. Besides
simplifying the API, the new list components also have significant
performance enhancements, the main one being nearly constant memory
usage for any number of rows.