Using Linked Lists in Checkerbox selection List react-native - javascript

I created a Linked-List data structure in my react-native app that I want to move between screens and then pick a node based on a checkerbox selection menu.
I understand that I can move the list using react-native-navigation, so now I would like to display the list with a checkerbox list to select multiple nodes and perform actions on them. The problem I see is that checkerbox lists use defined const arrays of items that are then listed.
The whole reason I went with linked-lists is that I need the list to dynamically update. (It may be more beneficial to use an array of large size instead, but each element within the node is somewhat large and I am unsure what effect a large array would have.)
Is there a way to input a linked list into a checkerbox list or would I have to create an array?
How would I go about either option if they need to dynamically update?

It may be more beneficial to use an array of large size instead, but each element within the node is somewhat large and I am unsure what effect a large array would have.
JavaScript arrays only hold references to the objects they store since it is a dynamic scripting language supporting prototype based object construction. Because of this, each element's size will not affect the array performance.
Another possibility is to extend the built-in Array class in your Linked-List data structure to ensure compatibility.
Your decision on using an Array or a Linked-List should be based on the List operations your app uses the most. There are a lot of articles about that.
Is there a way to input a linked list into a checkerbox list or would
I have to create an array?
How would I go about either option if they need to dynamically update?
There are some git repositories that add support for what you want to achieve (here is an example, feel free to explore npm for more).
Another possibility, if you want your items to dynamically update, will be to encapsulate them in a React.Component for rendering:
// YourNode.js
import React, { Component } from 'react';
import { CheckBox } from 'react-native';
export default class YourNode extends Component {
constructor(props) {
super(props);
this.state = {
checked: false,
};
}
selectItem = () => {
const { onPress } = this.props;
// Update the state
this.setState({ checked: !this.state.checked });
onPress();
};
render() {
const { node } = this.props;
// You may want to render more things like `Text` or `View` components based on
// the node's content
return (
<CheckBox
value={this.state.checked}
onValueChange={this.selectItem}
/>
);
}
}
import YourNode from './YourNode';
// YourScreen.js
render() {
//...
// This will render a `YourNode` component for each one of your nodes.
{yourList.map((item, index)) => {
return (<YourNode node={item} onPress{() => this.selectedNodes.push(item)}>)
}
}
}

Related

React how to display multiple components of different origin on the same parent component

I'm trying to build a Dashboard with React Js and I would like to know how you could display multiple components, but as widgets, which mean you need to be able to add them in any order and in any quantity. My problem is that I can't find a way to render a map of components.
The user may be able to add a widget to his dashboard by clicking a button, or remove them the same way, so I won't know how many of what components will be rendered
I would like to do something like, when the user clicks on a widget, it adds it to the dashboard, but I don't know how to do that.
I've tried to store components in a map, then with a forEach loop display them all by returning a div containing the component:
import Weather from '...'
import Currency from '...'
import News from '...'
const map = [Weather, Currency, News]
const runAll = () => {
map.forEach((fcn) => {
let runner = fcn
runner()
})
}
runAll()
I've searched many stack and other forums questions, without finding what I needed
Do you guys have an idea of what I could do to solve this ?
So you need to be able to easily render 2 things:
a list of widgets that the user can click and add in the dashboard
the actual dashboard. All selected widgets in a list (with a remove capability)
Let's first figure out what our state should be that also feeds the components 1 and 2.
For the 1st one we need the full list of available widgets. Since this is static (we have 3 widgets available) this can be expresses through a static mapping (a simple javascript object) declared once.
For the 2nd one we need an array of the user selected widgets. That's the dynamic part. We need to be able to set the initial widgets shown and have the capability to add and remove widgets from this list, allowing the same widget appearing more that once.
Static widget mapping
This should be a mapping between an identifier and the react widget component and should look like this:
import News from "./News";
import Weather from "./Weather";
import Currency from "./Currency";
const widgetsMapping = {
news: News,
weather: Weather,
currency: Currency
};
Widgets state
This is an array of widget identifiers (the keys from the static mapping) that the user wants in the dashboard. Also we need add and remove methods. Using useState we can write this like below:
const [widgets, setWidgets] = useState(["weather", "news"]);
const addWidget = (widget) => {
setWidgets([...widgets, widget]);
};
const removeWidget = (index) => {
const updated = [...widgets];
updated.splice(index, 1);
setWidgets(updated);
};
Rendering
Dashboard
Then we can render the dashboard by iterating our widget state array:
{widgets.map((widget, index) => {
const Widget = widgetsMapping[widget];
return (
<Widget
key={`${widget}${index}`}
removeWidget={() => removeWidget(index)}
/>
)
})}
removeWidget prop can be used to let a widget remove itself when sth is clicked.
List of available widgets
Here we will iterate through all available widgets from our static mapping and render all of them with the add functionality bound to them.
{Object.keys(widgetsMapping).map((widget) => (
<button key={widget} onClick={() => addWidget(widget)}>
{widget}+
</button>
))}
You can find a full working example in this code sandbox. Some assumptions were made about how you want to add and remove widgets but the main idea remains the same.
Keep a state (array) that holds widgets added by user. Define constants for widgets and save these constants to your persistance storage.
const widgets = {weather : 1, news: 2}
save these values to database as json with properties configured by user if needed, and then retrieve this json and render components based on it
sample JSON structure to save - [{type: 1, prop1: "val"},{type: 2, prop1: "val"}]
const renderWidgets = (array) => {
const widgets = [];
array.foreach((widget) => {
switch(widget) {
case widgets.weather:
widgets.push(<Weather ...props/>);
break;
.
.
.
etc
}
});
return widgets;
}

Using Map vs. Flatlist in React Native

I'm struggling to properly use .map() on a state object's array to display elements. Here's my code
class EventListFront extends Component {
state = {
currentEvent: new event(),
events: new eventList(),
}
...
<View>
{this.state.events.events.map(event => {
<View key={event.id}>{event.title}</View>})}
</View>
My Event structure is as follows:
class event {
constructor(
id = -1,
name = 'UNSET',
description = 'UNSET',
location = 'UNSET',
...
) {}
(eventList is an array of events, and it redundantly has an identifier 'events')
With this implementation, i have nothing showing when I add to the array. I've tried using a FlatList, but that requires keys/IDs for each element. I'm not sure how to
update the page with the new list when a new event is added
get the elements of events to be displayed in elements using .map()
use the generated ID so that it may properly render with a FlatList instead.
it seems there are several problems in your code :
as Tom Slutsky commented, you need to return the component in the callback that you pass to your map function. There are several cases for the return value in arrow functions depending on how you write them. Syntax of arrow functions
It seems that you are using always the same id in your events (-1), but when rendering a list of items, React requires to have unique keys for each component. If you update your state with set setState pushing a new item in your events array, your component will be re-rendered and updated.
Regarding the use of Flatlist, they have performance optimizations that are useful if you handle long lists of components. If you use map, all your list will be rendered even though components are not visible on the screen. With Flatlist, only components that are visible are rendered.

JavaScript / React - Async array map

I am using React Native. I want to create a very long list.
Each element in the List component is a ListItem component.
My problem is that the component is loading too much time. The List is created with Array.map().
Please have a look at the code.
My question is whether it is somehow possible to create async map() so elements in the list will load one by one and the component won't wait that much?
Tried something like this but nothing works for me. Can someone help please?
class List extends React.Component {
render() {
// very big array
var list = [...Array(5000).keys()];
return (
<Content>
{
list.map((item, index) => {
return (
<ListItem text={item} key={index}/>
);
})
}
</Content>
);
}
}
class ListItem extends React.Component {
render() {
return (
<Button>
<Text>{this.props.text}</Text>
</Button>
);
}
}
The async map code you linked to works, it just doesn't do what you want it to. The time it takes to render 5000 components from your array still takes a long time, async will just allow you to asynchronously do stuff on its completion.
From a UX perspective, you almost never need to render that many elements. Instead, you should load the data in as you need it (user scrolls down to a certain point).
Option 1
So the data flow logic would look something like:
Load first 100 entries to display.
User needs more entries
(scrolls down the list further) so you make a request
Load the
next 100 entries to display.
This is a common pattern if you are using an API to interface with your data.
Option 2
If you have the array in memory, you could try dividing the array into smaller chunks with Array.slice() and rendering each of those asynchronously.
Using async wont fix the issue. The size of array is big and mapping means running a for loop so for loop for let's say 5000 items will take time.
Restrict the size of array or increment them in bundles of 100 as you
go.

Filtering a list of names with ReactJS using data from RelayJS without calling GrpahQL

I'm stuck in wondering if I can filter a list of names which I receive from Relay and graphql-java server without the need of making calls, without making any changes in my GrpahQL schema and only using ReactJS for this purpose.
---MobX as a state management library can be a decision but I should first store all the Relay result.
Caveat emptor: I'm newish at Relay as well & struggling with these same concepts. But, given the relative dearth of accessible information on Relay, I thought it'd be helpful to try and layout the key concepts here. My understanding could be wrong, so I'd love it if anyone who found a mistake in my code/reasoning would comment/edit.
Filtering took awhile for me to 'click' as well. It depends on where you keep the data you'll use to filter, but let's assume the name field lives on your Users Type, and the query is something like this:
viewer {
allUsers {
edges {
node {
name
}
}
}
}
And let's say your top-level NameList component looked like this:
class NameList extends Component {
render() {
return (
<div>
{this.props.users.edges
.map(u => {
<NameItem name={u.node} />
})
}
</div>
)
}
}
Relay.createContainer(NameList, {
initialVariables: { first: 10 },
fragments: {
users: () => Relay.QL`
fragment on Viewer {
allUsers(first: $first) {
edges {
node {
${NameItem.getFragment('user')}
}
}
}
}
`
}
})
And your NameItem setup was simply:
class NameItem extends Component {
render() {
return (
<div>
Name: {this.props.user.name}
</div>
)
}
}
Relay.createContainer(NameItem, {
initialVariables: {},
fragments: {
user: () => Relay.QL`
fragment on User {
name
}
`
}
})
Consider the generalizable pattern here:
The List Component
A List component takes a fragment on the top-level Type in the query--in this case, Viewer, from a Relay container.
List also inserts a fragment on behalf of its Item child at the level of the User Type.
In other words, it captures an array of User objects it's supposed to pass down to the Item component.
If this wasn't Relay, and instead was, say, Redux, this component might simply pass state.users to the Item component. You can do that because, at some point, you've manually extracted all your Users from your own back-end and loaded them into Redux. But since Relay does the hard thinking for you, it needs a teensy bit more information than Redux.
The Item Component
This is even more simple. It expects an entity of type User and renders the name. Besides syntax, the functionality here isn't much different from a similar component in a Redux setup.
So really, without the complexity of Relay on top, all you have is an array of items that you're rendering. In vanilla React, you'd simply filter the array prior to (or during) your call to .map() in render().
However, with Relay, the fragment handed to the child is opaque to the parent--i.e., the List is handing a blind package to the Item, so it can't make a decision on whether or not to pass it down based on the fragment's content.
The solution in this contrived example is pretty simple: just peel-off the name field at the parent and child level. Remember: Relay is about components telling GraphQL what data they need. Your List component needs whatever fields it intends on filtering on--no more, no less.
If we modify the above List container:
...
users: () => Relay.QL`
fragment on Viewer {
allUsers(first: $first) {
edges {
node {
name
${NameItem.getFragment('user')}
}
}
}
}
`
And then we update our render function:
<div>
{this.props.users.edges
.map(u => {
if (u.node.name == "Harvey") {
<NameItem name={u.node} />
}
})
}
</div>
Then we've achieved basic filtering without needing mobx, more server trips, etc.

How to update Baobab leaf data before React component render?

ReactJS, Baobab, Material-UI app displays some items, identified by their numeric id. To display those, title and image url's are retrieved from a remote service via ajax. Tree branch stores that data:
data: {
12345: {title:'ABC', image:'https://...'}, // id is 12345
12346: {...
}
Upon item component creation and first rendering, its extended data may, or may not be already available in the tree. If its not, ajax call is enqueued to receive that data. It might happen that multiple items are created with the same item id.
To avoid extra requests for the same id, I want to put a dummy info {title:'loading', image:'spinner.gif'} into the tree upon the first request to that id's info. Thus this data will be used for the very first render(). Successive components would get that dummy info, and will not initiate any extra requests.
Question: how, and where can I place the code to test if the tree has no info yet and place the dummy there to indicate its "penging" state and enqueue the request?
Tried so far:
component's constructor – props are not set there yet;
componentWillMount() – the first render started with the old state of the tree, despite the tree.commit() after setting the dummy value;
in the branch function that dynamically creates components cursor pointing to its data. Got warning:
setState(...): Cannot update during an existing state transition (such as within render). Render methods should be a pure function of props and state.
This can be solved one level up – once the list of ids is available. But it feels right that a component should be able to handle its data within itself.
Please advice a correct way to immediately update Baobab tree data before the first render of a React Component, from within that Component?
In my case (i am use same stack) wrap branch work fine.
import BaobabPropTypes from 'baobab-react/prop-types';
class Actions {
/**
* #param {Baobab} tree
*/
static prefetchTree = (tree) => {
tree.select(somePath).set(defaultValue);
tree.commit();
};
}
class Page extends React.Component {
static contextTypes = {
tree: BaobabPropTypes.baobab
};
componentWillMount() {
Actions.prefetchTree(this.context.tree);
}
render() {
return <Branch {...this.props}/>;
}
}
Baobab has a get event, use it to detect requests that return values that are not fetched yet:
tree.on('get', function(e) {
if (e.data.data === undefined) {
const path = e.data.path; // requested cursor path like ['data',12345]
const id = path[1];
FETCH_DATA(id)
.then( data => tree.set(path , data) );
tree.set(path, PLACEHOLDER_DATA);
}

Categories