How use N views in sample app in react native - reactjs

I have some doubt in React Native. I read this answer (How to do a multi-page app in react-native?) but I stayed with a little bit doubt:
I use Navigator to many views in React Native App, but how I do to N componentes? For example, if I have 5 different views I have use the before code five times... and n times?

to ellude more on my comment.
instead of this
if (routeId === 'SplashPage') {
return (
<SplashPage
navigator={navigator}/>
);
}
if (routeId === 'LoginPage') {
return (
<LoginPage
navigator={navigator}/>
);
}
just have a hashtable that you use to get the component dynamically.
const Component = VIEW_COMPONENTS[routeid];
so your code would look something like this
const VIEW_COMPONENTS = {
'SplashPage': SplashPage,
'LoginPage': LoginPage
};
renderScene = ( route, navigator ) => {
const ViewComponent = VIEW_COMPONENTS[route.id];
return <ViewComponent navigator={navigator}/>
}
any additional screen would be a single line entry to your lookup table. I have a native app with 40 screens and its very easy to manage like this
to add to this. you can abstract more out of this too. make every view not care about where it is used or what its next view is. Instead make all of that a part of your lookup object. specify a next route that every view can show. you can pass any additional information all of which is configurable and your screens can be reused on multiple flows!

Related

Algolia: Export React Instant Search results

Is there any way to export Algolia's React Instant Search results to a CSV? I've tried using the react-csv package, but it doesn't work with Algolia's Hit Component. The package requires data as props, but the data is constantly changing since it's React Instant Search.
What I mean by constantly changing is that on page load, you're given the entire index of records found, then you can narrow down the results with the search bar or other filtering components.
I've gone down the Google rabbit hole looking for information about exporting Algolia's search results as a CSV, but I haven't found anything regarding React Instant Search—unless I completely missed it.
Has anyone tried this before? If so, could you point me in the right direction regarding documentation or examples?
Not sure if this solves your problem but one possible way is to use the StateResults widget. The StateResults widget provides a way to access the searchState and the searchResults of InstantSearch.
Here I will create a custom StateResults component in the form of a download button and then connect it using the connectStateResults connector.
I have attached a demo below as well.
For simplicity I didn't format the data to be fed into the CSV builder.
// 1. Create a React component
const StateResults = () => {
// return the DOM output
};
// 2. Connect the component using the connector
const CustomStateResults = connectStateResults(StateResults);
// 3. Use your connected widget
<CustomStateResults />
In your case something like
const StateResults = ({ searchResults }) => {
const hits = searchResults?.hits;
return (
<div>
<button>{hits && <CSVLink data={hits}>Download CSV</CSVLink>}</button>
</div>
);
};
const DownloadButton = connectStateResults(StateResults);
//in your JSX then <DownloadButton />

Navigating through a multi layered react app

I am currently building a react app that has a multi layered component structure where users navigate down into deeper layers. The user would start at the top parent level and then navigate down into deeper and deeper layers of the app (and can also go back up into each previous layer). The layers follow a linear structure (e.g. 1,2,3,4) so there is always a predefined route that users follow, and users could navigate back and forth with a 'next' and 'previous' button. The structure would look like this:
Parent level --> sub level 1 --> sub level 2 --> sub level 3
So each subsequent layer is beneath the previous one. The app itself will render content that gets more detailed and fine grained in each subsequent layer.
My question is how best to represent this structure via components. One option would be to keep track of the current layer via a 'layer' state, and then to show the component that corresponds to the current layer state (as shown in the example code below).
I am unsure whether this is the optimal solution for the structure of my app. I would be very grateful for any suggestions.
Thank you.
import React, { useState } from 'react';
const App = () => {
const [layer, setLayer] = useState(1);
const nextLayer = () => {
setLayer(layer + 1);
}
const prevLayer = () => {
if (layer === 1) {
return;
}
setLayer(layer - 1);
}
let currentLayerView;
switch (layer) {
case 1:
currentLayerView= (
<Layer1
nextLayer={nextLayer}
prevLayer={prevLayer}
/>
);
break;
case 2:
currentLayerView= (
<Layer2
nextLayer={nextLayer}
prevLayer={prevLayer}
/>
);
break;
case 3:
currentLayerView= (
<Layer3
nextLayer={nextLayer}
prevLayer={prevLayer}
/>
);
break;
default:
console.log('default');
}
return (
<>
{currentLayerView}
</>
);
};
export default App;

ReactJS - Async Dynamic Component Loading

Situation
I receive json from a cms that describes the content that needs to display on any given page. This project has 50+ components so rather than require all of them on every page I'd rather cherry pick them as needed.
Question
How can I
Make sure all components are available for import (I assume this requires some webpack trickery)
When converting the json's content node to jsx, making sure that any component described is rendered out.
Current Thoughts
I can loop through the raw jsx and collect all the tags for a given page then attempt a load for each tag via something like
const name = iteration.tagName;
dynCmps[name] = someAsynchronousLoad(path + name);
Then dispatch a redux event when loading is complete to kick off a fresh render of the page.
As for converting raw text content to react js I'm using ReactHtmlParser
best resources so far
Dynamic loading of react components
http://henleyedition.com/implicit-code-splitting-with-react-router-and-webpack/
This had me stumped for a couple of days. After chatting with a colleague about it for some time it was decided that the amount of work it would take to offload the performance hit of loading all the components upfront is not work it for our scenario of 30-50 components.
Lazy loading CAN BE used but I decided against it as the extra 10ms of loading (if that) isn't going to be noticeable at all.
import SomeComponent from "./SomeComponent.js"
const spoofedComponents = {
SomeComponent: <SomeComponent />
}
const replaceFunc = (attribs, children) => {
const keys = Object.keys(spoofedComponents);
for(var i in keys) {
const key = keys[i];
// lower case is important here because react converts everything to lower case during text-to-html conversion - only react components can be camel case whereas html is pascal case.
if(attribs.name === key.toLowerCase()) {
return spoofedComponents[key];
}
}
return <p>unknown component</p>
}
...
//inside render
const raw = "<SomeComponent><SomeComponent />"
// it's VERY important that you do NOT use self-closing tags otherwise your renders will be incomplete.
{parse(raw, {
replace: replaceFunc
})}
In my case I have 30+ components imported and mapped to my spoofedComponents constant. It's a bit of a nuissance but this is necessary as react needs to know everything about a given situation so that the virtual dom can do what it is supposed to - save on display performance. The pros are that now a non-developer (editor) can build a layout using a WYSIWYG and have it display using components that a developer made.
Cheers!
Edit
I'm still stuck on adding customized props & children.
Edit
Basic props are working with
const spoofedComponents = {
SomeComponent: (opts) => {
let s = {};
if(opts.attribs.style)
s = JSON.parse(opts.attribs.style);
if(opts.attribs.classname) {
opts.attribs.className = opts.attribs.classname;
delete opts.attribs.classname;
}
return <APIRequest {...opts.attribs} style={s}>{opts.children[0].data}</APIRequest>
}
}
...
const replaceFunc = (opts) => {
const keys = Object.keys(spoofedComponents);
for(var i in keys) {
const key = keys[i];
if(opts.name === key.toLowerCase()) {
const cmp = spoofedComponents[key](opts);
return cmp;
}
}
return <p>unknown component</p>
}
Now to figure out how to add child components dynamically..
EDIT
This is working well enough that I'm going to leave it as is. Here is the updated replaceFunc
const replaceFunc = (obj) => {
const keys = Object.keys(spoofedComponents);
for(var i in keys) {
const key = keys[i];
if(obj.name === key.toLowerCase()) {
if(obj.attribs.style)
obj.attribs.style = JSON.parse(obj.attribs.style);
if(obj.attribs.classname) {
obj.attribs.className = obj.attribs.classname;
delete obj.attribs.classname;
}
return React.createElement(spoofedComponents[key], obj.attribs, obj.children[0].data)
}
}
return obj; //<p>unknown component</p>
}

Backbone => React - Higher Order Components, inheritance and specialisation

I have a legacy Backbone app which I have begun to rewrite in React. The app has a main view containing two subviews, arranged vetically. The top panel displays some data, and the bottom one displays the result of some algorithm taking this data as input. Since I have many different data sources, each with a different algorithm applied to it, I have an abstract base View class, which I then subclass for each data source, adding, decorating and overriding methods as necessary. Somewhat like this:
// Base View.
const BaseView = Backbone.View.extend({
events: {},
initialize() {
this.subViewA = // instantiate subview...
this.subViewB = // instantiate subview...
},
generateResultData() {
// 'Abstract' method which should be specialised to generate data rendered by subViewB...
},
render() {
// render subviews...
},
});
// Derived View.
const Derived = BaseView.extend({
events: {
// event handlers...
},
add(a, b) {
return a+b;
},
// additional methods...
generateResultData() {
return {
result: this.add(2,2);
}
},
})
This results in a shallow hierarchy of many similar View classes. It's all terribly imperative, but it's a simple, intuitive and easy-to-reason-about pattern, and just works. I'm struggling to see how to achieve the same thing in React, however. Given that subclassing of subclasses of React.Component is considered an anti-pattern, my focus has naturally been on composition, and in particular Higher Order Components. HOCs (which I find beautiful, but unintuitive and often just downright confusing) seem to involve adding general features, rather than specialising/refining something more general. I have also considered passing in more specialised versions of Componenet methods through props. but that just means I have to use the same boilerplate Component definition over and over again:
// General functional component, renders the result of prop function 'foo'.
function GeneralComponent(props) {
const foo = this.props.foo || ()=>"foo";
return (
<div>
<span> { this.props.foo() } </span>
</div>
)
}
// Specialised component 1, overrides 'foo'.
class MySpecialisedComponent extends React.Component {
foo() {
return this.bar()
}
bar() {
return "bar"
}
render() {
return (
<GeneralComponent foo={this.foo} />
)
}
}
// Specialised component 2, overrides 'foo' and adds another method.
class MyOtherSpecialisedComponent extends React.Component {
foo() {
return this.bar() + this.bar()
}
bar() {
return "bar"
}
baz() {
return "baz"
}
render() {
return (
<GeneralComponent foo={this.foo} />
)
}
}
The above is a very simplistic case, obviously, but essentially captures what I need to do (though I would of course be manipulating state, which the example does not do, for simplicity). I mean, I could just do things like that. But I want to avoid having to repeat that boilerplate all over the place. So is there a simpler and more elegant way of doing this?
Generally, if a component is stateless and doesn't use lifecycle hooks, there are no reasons for it to be Component class. A class that acts as a namespace and doesn't hold state can be considered an antipattern in JavaScript.
In constrast to some other frameworks, React doesn't have templates that would need to map variables in order for them to be available in view, so the only place where bar function needs to be mentioned is the place where it's called. JSX is an extension over JavaScript, JSX expressions can use any names that are available in current scope. This allows to compose functions without any classes:
const getBar => "bar";
const getBaz => "baz";
const getBarBaz => getBar() + getBaz();
const MySpecialisedComponent = props => <GeneralComponent foo={getBar} />;
const MyOtherSpecialisedComponent = props => <GeneralComponent foo={getBarBaz} />;
An anonymous function could be passed as foo prop instead of creating getBarBaz but this is generally discouraged because of unnecessary overhead.
Also, default prop values could be assigned with defaultProps without creating new ()=>"foo" function on each component call:
function GeneralComponent({ foo }) {
return (
<div>
<span> {foo()} </span>
</div>
)
}
GeneralComponent.defaultProps = { foo: () => 'foo' };
IMO what is throwing you off isn't inheritance vs composition, it's your data flow:
For example, many of my derived views need to do custom rendering after the main render. I'm using a third-party SVG library, and the data rendered into the 'result' subview is derived from analysis of rendered SVG elements in the main data view above it
So what you're trying to do here is have a child update props of a distantly related component after render, correct? Like this?
// after the svg renders, parse it to get data
<div id="svg-container">
<svg data="foo" />
<svg data="bar />
</div>
// show parsed data from svg after you put it through your algos
<div id="result-container">
// data...
</div>
There's a lot of state management libraries out there that will help you with this problem, that is, generating data in one component and broadcasting it to a distantly related component. If you want to use a tool built-in to react to address this you may want to use context, which gives you a global store that you can provide to any component that wants to consume it.
In your example your child classes have data-specific methods (add, etc.). IMO it's more typical in react to have a generic class for displaying data and simply passing it down map functions as props in order to rearrange/transform the rendered data.
class AbstractDataMap extends PureComponent {
static defaultProps = {
data: [],
map: (obj, i) => (<div key={i}>{obj}</div>)
};
render() {
const { data, map, children } = this.props;
const mapped = data.map(map);
return (
<Fragment>
{mapped.map((obj, i) => (
children(obj, i)
))}
</Fragment>
);
}
}
// in some other container
class View extends Component {
render() {
return (
<div>
<AbstractDataMap data={[1, 2, 3]} map={(n) => ({ a: n, b: n + 1 })}>
{({ a, b }, i) => (<div key={i}>a: {a}, b: {b}</div>)}
</AbstractDataMap>
<AbstractDataMap data={[2, 4, 6]} map={(n) => (Math.pow(n, 2))}>
{(squared, i) => (<div key={i}>squared: {squared}</div>)}
</AbstractDataMap>
</div>
);
}
}
IMO this pattern of using an HOC to abstract away the labor of explicitly using .map in your render calls (among other uses) is the pattern you are looking for. However, as I stated above, the HOC pattern has nothing to do your main issue of shared data store across sibling components.
Answering my own question, which I've never donw before...
So my question really arose from a concern that I would need to refactor a large, imperative and stateful codebase so as to integrate with React’s composition-based model (also with Redux). But it occurred to me after reading the (very insightful and helpful) responses to my question that my app has two parallel parts: the UI, and an engine which runs the algorithms (actually it's a music analysis engine). And I can strip out the Backbone View layer to which the engine is connected quite easily. So, using React’s context API I've built an ‘AnalysisEngineProvider', which makes the engine available to subcomponents. The engine is all very imperative and classically object-oriented, and still uses Backbone models, but that makes no difference to the UI as the latter has no knowledge of its internals - which is how it should be (the models will likely be refactored out at some point too)...
The engine also has responsibility for rendering the SVG (not with BB views). But React doesn’t know anything about that. It just sees an empty div. I take a ref from the div and pass it to the engine so the latter knows where to render. Beyond that the engine and the UI have little contact - the divs are never updated from React state changes at all (other components of the UI are though, obviously). The models in the engine only ever trigger updates to the SVG, which React knows nothing about.
I am satisfied with this approach, at least for now - even if it's only part of an incremental refactor towards a fully React solution. It feels like the right design for the app whatever framework I happened to be using.

Best practice for dynamic routing (react router v4) needed?

With React Router V4 being out only for a little while and there being no clear documentation on dynamic routing [akin to transtionsTo(...) in V3] I feel like a simple answer to this question could benefit many. So here we go.
Lets assume the following theoretical scenario: one has a component Container, which includes two other components (Selection and Display). Now in terms of functionality:
Container holds a state, which can be changed by Selection, Display shows data based on said state.
Now how would one go about changing the URL as well as the state triggered by a change in state via react router?
For a more concrete example please see (React Router V4 - Page does not rerender on changed Route). However, I felt the need to generalize and shorten the question to get anywhere.
Courtesy to [Tharaka Wijebandara] the solution to this problem is:
Have the Container component provide the Selection component with a callback function that has to do at least the following on Container:
props.history.push(Selection coming from Selection);
Please find below an example of the Container (called Geoselector) component, passing the setLocation callback down to the Selection (called Geosuggest) component.
class Geoselector extends Component {
constructor (props) {
super(props);
this.setLocation = this.setLocation.bind(this);
//Sets location in case of a reload instead of entering via landing
if (!Session.get('selectedLocation')) {
let myRe = new RegExp('/location/(.*)');
let locationFromPath = myRe.exec(this.props.location.pathname)[1];
Session.set('selectedLocation',locationFromPath);
}
}
setLocation(value) {
const newLocation = value.label;
if (Session.get('selectedLocation') != newLocation) {
Session.set('selectedLocation',newLocation);
Session.set('locationLngLat',value.location);
this.props.history.push(`/location/${newLocation}`)
};
}
render () {
return (
<Geosuggest
onSuggestSelect={this.setLocation}
types={['(cities)']}
placeholder="Please select a location ..."
/>
)
}
}

Resources