React Native List Component that looks like iOS native lists - reactjs

Does anybody know if there is any built-in React Native component that render lists like this?

It shouldn't be super hard to implement this from scratch
If you want the exactly same design there are probably three or so components that you need to write
Heres pseudo code
const settings = [
{
items : [{
icon : SomeIcon,
label : "Notifications",
action: () => ...navigate somewhere
},
...more items
]
}
]
...more code
return
<FlatList>
{settings.map(setting =>{
return <SettingSection>
{setting.items.map(item =>{
return <Item/>
})
}
</SettingSection>
})}
</FlatList>

You can try to do it by yourself but if you want exactly the same view, here i found a npm package for you. https://www.npmjs.com/package/react-native-settings-list . it is efficient and customizable.

Related

React phaser Reinitialize problem because of grid-engine plugin

I use "phaser", 'ion-phaser' and a phaser plugin 'grid-engine' for integrate a phaser game to react.
I can initialize the game successfully, then destroy it without an error. But when I try to reinitialize it İt give 2 errors
Scene Plugin key in use: GridEngine
and
Uncaught TypeError: Cannot read properties of undefined (reading 'create')
at Scene.create (isometric.js:93:1)
at SceneManager.create (phaser.js:84684:1)
at SceneManager.loadComplete (phaser.js:84615:1)
at LoaderPlugin.emit (phaser.js:1671:1)
at LoaderPlugin.loadComplete (phaser.js:165337:1)
at LoaderPlugin.fileProcessComplete (phaser.js:165311:1)
at SpriteSheetFile.onProcessComplete (phaser.js:4290:1)
at data.onload (phaser.js:16548:1)
second error at this line
this.gridEngine.create(cloudCityTilemap, gridEngineConfig);
Additionally when I try a phaser game without plugins like grid engine, there is not an error.
I search and I think I must remove grid-engine plugin when destroy game but I could not find how.
My question is
How to destroy a phaser game with included plugins and restart it?
this is code sample
const game = {
width: 800,
height: 600,
type: Phaser.AUTO,
scene: {
preload,
create,
update,
},
plugins: {
scene: [
{
key: 'GridEngine',
plugin: GridEngine,
mapping: 'gridEngine'
},
],
},
}
const gameRef = useRef(null)
const [initialize, setInitialize] = useState(false)
const destroy = () => {
if (gameRef.current) {
gameRef.current.destroy()
}
setInitialize(false)
}
return (
<>
<IonPhaser game={game} ref={gameRef} initialize={initialize} />
<button onClick={()=>setInitialize(true)}>Initialize</button>
<button onClick={destroy}>Destroy</button>
</>
)
}
Finally I want to change different games or levels at the same page
Im not 100% sure, if this will solve all your problems, but
<button onClick={setInitialize(true)}>
is not correct, you should not call the function, you should only pass it.
Try replacing that line with
<button onClick={ () => setInitialize(true)}>
For Details, checkout this article / documentation for details.
Update: How to get the game object
to get the Phaser Game object you would have to use getInstance (link to ion-phaser documentation) and to remove the Plugin you would have to do something like this:
this.gameRef.current.getInstance()
.then(game => game.plugins.removeScenePlugin("GridEngine"))

react native Why does my async code hang the rendering?

General :
TL;DR: async code hangs rendering.
I have this component with a Modal and inside the Modal it renders a list of filters the user can choose from. When pressing a filter the color of the item changes and it adds a simple code(Number) to an array. The problem is that the rendering of the color change hangs until the logic that adds the code to the array finishes.
I don't understand why adding a number to an array takes between a sec and two.
I don't understand why the rendering hangs until the entire logic behind is done.
Notes: I come from a Vue background and this is the first project where I'm using react/react-native. So if I'm doing something wrong it would be much appreciated if someone points that out
Snack that replicates the issue :
Snack Link
My code for reference :
I use react-native with expo managed and I use some native-base components for the UI.
I can't share the whole code source but here are the pieces of logic that contribute to the problem :
Parent : FilterModal.js
The rendering part :
...
<Modal
// style={styles.container}
visible={modalVisible}
animationType="slide"
transparent={false}
onRequestClose={() => {
this.setModalVisible(!modalVisible);
}}
>
<Center>
<Pressable
onPress={() => this.setModalVisible(!modalVisible)}
>
<Icon size="8" as={MaterialCommunityIcons} name="window-close" color="danger.500" />
</Pressable>
</Center>
// I use sectionList because the list of filters is big and takes time to render on the screen
<SectionList
style={styles.container}
sections={[
{ title: "job types", data: job_types },
{ title: "job experience", data: job_experience },
{ title: "education", data: job_formation },
{ title: "sector", data: job_secteur }
]}
keyExtractor={(item) => item.id}
renderItem={({ item, section }) => <BaseBadge
key={item.id}
pressed={this.isPressed(section.title, item.id)}
item={item.name}
code={item.id}
type={section.title}
add={this.addToFilters.bind(this)}
></BaseBadge>}
renderSectionHeader={({ section: { title } }) => (
<Heading color="darkBlue.400">{title}</Heading>
)}
/>
</Modal>
...
The logic part :
...
async addToFilters(type, code) {
switch (type) {
case "job types":
this.addToTypesSelection(code);
break;
case "job experience":
this.addToExperienceSelection(code);
break;
case "formation":
this.addToFormationSelection(code);
break;
case "sector":
this.addToSectorSelection(code);
break;
default:
//TODO
break;
}
}
...
// the add to selection methods look something like this :
async addToTypesSelection(code) {
if (this.state.jobTypesSelection.includes(code)) {
this.setState({ jobTypesSelection: this.state.jobTypesSelection.filter((item) => item != code) })
}
else {
this.setState({ jobTypesSelection: [...this.state.jobTypesSelection, code] })
}
}
...
Child :
The rendering Part
render() {
const { pressed } = this.state;
return (
< Pressable
// This is the source of the problem and read further to know why I used the setTimeout
onPress={async () => {
this.setState({ pressed: !this.state.pressed });
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
<Badge
bg={pressed ? "primary.300" : "coolGray.200"}
rounded="md"
>
<Text fontSize="md">
{this.props.item}
</Text>
</Badge>
</Pressable >
);
};
Expected outcome :
The setState({pressed:!this.state.pressed}) finishes the rendering of the item happens instantly, the rest of the code happens after and doesn't hang the rendering.
The change in the parent state using the add code to array can happen in the background but I need the filter item ui to change instantly.
Things I tried :
Async methods
I tried making the methods async and not await them so they can happen asynchronously. that didn't change anything and seems like react native ignores that the methods are async. It hangs until everything is done all the way to the method changing the parent state.
Implementing "event emit-listen logic"
This is the first app where I chose to use react/react-native, coming from Vue I got the idea of emitting an event from the child and listening to it on the parent and execute the logic that adds the code to the array.
This didn't change anything, I used eventemitter3 and react-native-event-listeners
Using Timeout
This is the last desperate thing I tried which made the app useable for now until I figure out what am I doing wrong.
basically I add a Timeout after I change the state of the filter component like so :
...
< Pressable
onPress={async () => {
// change the state this changes the color of the item ↓
this.setState({ pressed: !this.state.pressed });
// this is the desperate code to make the logic not hang the rendering ↓
setTimeout(() => {
this.props.add(this.props.type, this.props.code);
});
}}
>
...
Thanks for reading, helpful answers and links to the docs and other articles that can help me understand better are much appreciated.
Again I'm new to react/react-native so please if there is some concept I'm not understanding right point me in the right direction.
For anyone reading this I finally figured out what was the problem and was able to solve the issue for me.
The reason the rendering was getting hang is because the code that pushes to my array took time regardless of me making it async or not it was being executed on the main thread AND that change was triggering screen re-render which needed to wait for the js logic to finish.
The things that contribute to the solution and improvement are :
Make the array (now a map{}) that holds the selected filters stateless, in other words don't use useState to declare the array, instead use good old js which will not trigger any screen re-render. When the user applies the filters then push that plain js object to a state or context like I'm doing and consume it, doing it this way makes sure that the user can spam selecting and deselecting the filters without hanging the interactions.
first thing which is just a better way of doing what I needed is to make the array a map, this doesn't solve the rerender issue.

Change material of gltf-imported mesh programmatically - react three fiber

I'm currently working at a project with react-three-fiber. I've imported the model with useGLTF from #react-three/drei.
With
const { nodes, materials } = useGLTF('/model.glb');
I access the materials from the glb-file.
To access and manipulate the model I used gltfjsx to generate the model.
Now I need to change the material of a mesh programmatically. Because I have no direct access to the JSX of the model I do it with React.cloneElement and modify the props of the mesh.
So I tried something like this:
return React.cloneElement(mesh, {
material: overwriteMaterial ?
<meshStandardMaterial attach="material" color={0xa3005c} metalness={1} roughness={0.2} visible={true} /> :
materials['mat_7']
});
If overwriteMaterial is false it works. It shows the material it should. But if it's true then the mesh disappears.
I also thought of putting the <meshStandardMaterial /> in the children prop of the mesh. Something like so:
return React.cloneElement(mesh, {
material: overwriteMaterial ? undefined : materials['mat_7'],
children: overwriteMaterial ? <meshStandardMaterial attach="material" color={0xa3005c} metalness={1} roughness={0.2} visible={true} /> : undefined
});
With this I always get this error and I don't know why it appears:
TypeError: Cannot read properties of undefined (reading 'visible')
Could this approach somehow work or am I doing something completely wrong?
Every help is welcome. Thanks
Alright so I found the answer by myself after some more hours searching for a solution.
The material property doesn't accept a JSX-tag. So if you create an instance of the class MeshStandardMaterial you can pass it to the property and it works perfectly fine. Now it looks something like this:
return React.cloneElement(mesh, {
material: overwriteMaterial
? new MeshStandardMaterial({ color: 0x0ff000 })
: materials['mat_7']
})
Note: The class MeshStandardMaterial is exported from the three package.
i really don't think you have to clone any react element, that doesn't seem correct. you can clone or mutate materials just as you would in a plain three app. i have no idea why you even want to clone jsx.
const { scene } = useGLTF(url)
const clonedScene = useMemo(() => scene.clone(), [])
useLayoutEffect(() => {
clonedScene.traverse(o => {
if (o.type === 'Mesh') {
o.material = ...
}
})
}, [clonedScene]}
return <primitive object={clonedScene} />
you can skip the clonedScene thing completely as well, this is only if you plan to re-use the model in your scene multiple times.

Creating a custom Leaflet layer control in React

I'm attempting to completely recreate or reorganize the functionality of the LayersControl component in its own separate panel using react-leaflet.
I have several filtered into their own and it works fine, but I'd like to customize the look and location of the Control element.
I've hosted the current version of my Leaflet app on github pages here. You can see the control on the right, which is the basic Leaflet control, but I'd like to the Icon on the left (the layers icon) to accomplish the same thing instead with custom react components.
Just wondering if anyone can point me in the right direction to beginning to accomplish this!
This is my current render for my react-leaflet map:
render() {
const types = [...new Set(data.map(loc => loc.type))];
const group = types.map(type =>
data.filter(loc => loc.type === type)
.map(({id, lat, lng, name}) =>
<LayersControl.Overlay name={startCase(toLower(type))}>
<LayerGroup>
<Marker key={id} position={[lat, lng]} icon=
{locationIcon}>
<Tooltip permanent direction="bottom" opacity={.6}>
{name}
</Tooltip>
</Marker>
</LayerGroup>
</LayersControl.Overlay>
));
return (
<>
<ControlPanel />
<Map
zoomControl={false}
center={this.state.center}
zoom={this.state.zoom}
maxBounds={this.state.maxBounds}
maxZoom={10}
>
<LayersControl>
<TileLayer
url='https://cartocdn-gusc.global.ssl.fastly.net//ramirocartodb/api/v1/map/named/tpl_756aec63_3adb_48b6_9d14_331c6cbc47cf/all/{z}/{x}/{y}.png'
/>
<ZoomControl position="topright" />
{group}
</LayersControl>
</Map>
</>
);
}
So theres still a few bugs in this but i've managed get most of the way (self taught react) using material UI as an example, can be seen in this sandbox link:
https://codesandbox.io/embed/competent-edison-wt5pl?fontsize=14
The general bassis is that we extend MapControl which means we have to define createLeafletElement, this has to return a generic leaflet (not react) control from the original javascript leaflet package. Essentially making a div with the domutil provided by leaflet and then portaling our react components through that div with react portals.
Again with another class extension we extend some of the classes provided by react-leaflet for layers, i pulled it out and just made a generic layer that you could define a group for, that way you could render any layer (polygon, baselayer etc) and specify the group to tell it where to go in the layer control i.e no need for specific components or overlays. As we are extending the class we need implement and pass down the methods we want to use, like addLayer, remove layer etc. During these implementations i've just added them to state to track what layers are active and such.
Not sure if there are better practices throughout everything i've implemented but this is definitely a start, hopefully in the right direction.
Bugs - The first layer in each group won't turn on correctly without the 2nd item ticked, something to do with state i think but didn't have the time to track it down
Thanks Dylan and Peter for this nice React Leaflet custom control approach. I assumed there was still a bug in the toggleLayer function. It's checked multiple checkboxes and the layers won't change properly. So I restructered a little bit and now it should work fine.
toggleLayer = layerInput => {
const { name, group } = layerInput;
let layers = { ...this.state.layers };
layers[group] = layers[group].map(l => {
l.checked = false;
this.removeLayer(l.layer);
if (l.name === name) {
l.checked = !l.checked;
this.props.leaflet.map.addLayer(l.layer);
}
return l;
});
this.setState({
layers
});
};
Just to elaborate on the bug that is mentioned in Dylans answer...
If you have more then one ControlledLayerItem, none items are added to the map until the very last item is checked. To fix this, the toggleLayer method in ControlLayer2.js has to be slightly modified:
toggleLayer = layerInput => {
const { layer, name, checked, group } = layerInput;
let layers = { ...this.state.layers };
layers[group] = layers[group].map(l => {
if (l.name === name) {
l.checked = !l.checked;
l.checked
? this.props.leaflet.map.addLayer(layer)
: this.removeLayer(layer);
}
return l;
});
this.setState({
layers
});
};
Thanks Dylan for the code, it was really helpfull.

How do properly use Onsen's Navigator in React?

I'm trying to implement a simple Onsen Navigator in React.
So far I'm receiving an error 'route is not defined' and I was looking through the examples & docs but I only saw the initialRoute prop was provided, how & where does the route prop generated or something? Cause it seems like its not specified.
Here is my the code of my component:
import React, {PropTypes} from 'react';
import ons from 'onsenui';
import * as Ons from 'react-onsenui';
class SignUp extends React.Component {
constructor(props) {
super(props);
this.state = {
index : 0
};
this.renderPage = this.renderPage.bind(this);
this.pushPage = this.pushPage.bind(this);
}
pushPage(navigator) {
navigator.pushPage({
title: `Another page ${this.state.index}`,
hasBackButton: true
});
this.setState({index: this.state.index++});
}
renderPage(route, navigator) {
return (
<Ons.Page key={route.title}>
<section style={{margin: '16px', textAlign: 'center'}}>
<Ons.Button onClick={this.pushPage}>
Push Page
</Ons.Button>
</section>
</Ons.Page>
);
}
render() {
return (
<Ons.Page key={route.title}>
<Ons.Navigator
renderPage={this.renderPage}
initialRoute={{
title: 'First page',
hasBackButton: false
}}
/>
</Ons.Page>
);
}
};
SignUp.propTypes = {
'data-pageName': PropTypes.string.isRequired
};
export default SignUp;
Is this the right syntax in ES6? Have I missed something?
When using Ons.Navigator in react the two required properties are:
initialRoute - it should be an object.
renderPage - method which receives 2 arguments - route and navigator. The route should be an object similar to the initialRoute one. You provide that object when you are calling pushPage and similar methods.
It seems that you already know these 2, but there still 2 other things which you need to be careful about. They are not directly onsen related, but come up a lot when using react in general.
Whenever you have a list of dom elements (for example an array of Ons.Page tags) each of those should have a unique key property.
Whenever you use a method you need to make sure you are binding it if you need some extra arguments.
It seems you also know these two. So the only thing left is to make sure you follow them.
Your syntax is correct - the only thing missing is the route variable in SignUp.render. Maybe you originally copied the renderPage method and that is how you have a leftover Ons.Page.
If you're not putting the SignUp component inside some other navigator, tabbar or splitter then you don't actually need the Ons.Page in its render method. Those are the only cases when they are needed. If you it happens to have one of those components as a parent then you can just specify the key.
PS: I think there should be a React Component Inspector (something like this) which you can install - then I think you may be able to see the place where the error occurs. I think if you knew on which line the problem was you would have been able to solve it. :)
For me, with the object I was passing to initialRoute(), it needed a props property, which itself was an object with a key property. See the before and after below.
Before fixing
render() {
return (
<Navigator
initialRoute={{component: DataEntryPage}}
renderPage={this.renderPage}
/>
);
}
}
This was causing the following console warning:
Warning: Each child in an array or iterator should have a unique "key" prop.
Check the render method of `Navigator`.
After fixing
render() {
return (
<Navigator
initialRoute={{component: DataEntryPage, props: {key: 'DataEntryPage'}}}
renderPage={this.renderPage}
/>
);
}
}
Notice that the difference I needed to make was the addition of , props: {key: 'DataEntryPage'}.
Feel free to check out this medium article for more information.

Resources