Multiple navigation.push same screen always go to the last of the list - react-native

I have a screen to display news articles. In a news there can be reference to other articles. On clicking on the reference i go to another news detail screen which display the new articles.
If have only one reference to another article, it works fine, even go back.
But if i have more reference, i always go to the last news article and not to the specific news article i am clicking on. The reference is the "id" of the article, pass via the param of a navigation.push, inside a .
Why is it always the last news id that is used ? I put the id (infNum) in the area, and it references the correct id of each article, but it seems that the param idNews is always the last infNum.
Here is my code :
'''
{news.results.texte.map((content, index) => {
if(content.substring(0,6) === '_Info_') {
{
finNum = content.indexOf("_T_");
finLien = content.indexOf("_/Info_");
infNum = content.substring(6,finNum);
txtLien = content.substring(finNum+3,finLien)
}
//console.log(finNum," ",finLien," ",infNum," ",txtLien)
return (
<TouchableOpacity
style={styles.txtLien}
key={infNum}
onPress={() => {
navigation.push("NewsDetail", {idNews:infNum})}
}
>
<Text>{txtLien}-{infNum}</Text>
</TouchableOpacity>
)
} else {
return (
<Text style={styles.description_text} key={index}>{content}</Text>
)
}
'''

I found the problem. Basic Javascript. The variable 'infNum' as it is not declared with the 'var' keyword is considered as global and so takes the last value given. I just put 'var infNum = content.substring ....' and it works well

Related

Vuex model update won't reload computed property

I have the following component to quickly configure stops on a delivery/pickup route and how many items are picked up and dropped
and this is the data model, note the 2 is the one next to 'a' on the previous image.
If a click the + or - button, in the first item, it behaves as expected,
But second item doesn't work as expected
I've already checke a couple of posts on object property update likes this ones
Is it possible to mutate properties from an arbitrarily nested child component in vue.js without having a chain of events in the entire hierarchy?
https://forum.vuejs.org/t/nested-props-mutations-hell-internet-need-clarification/99346
https://forum.vuejs.org/t/is-mutating-object-props-bad-practice/17448
among others, and came up with this code:
ADD_ITEM_TO_SELECTED_STOP(state, payload) {
let count = state.selectedStop.categories[payload.catIndex].items[payload.itemIndex].count;
const selectedCat = state.selectedStop.categories[payload.catIndex];
const currentItem = selectedCat.items[payload.itemIndex];
currentItem.count = count + 1;
selectedCat.items[payload.itemIndex] = currentItem;
Vue.set(state.selectedStop.categories, payload.catIndex, selectedCat);
},
and as the button event:
addToItem(item) {
this.$store.dispatch("addItemToSelectedStop", {
catIndex: item.catIndex,
itemIndex: item.itemIndex
})
},
And finally my computed property code:
items() {
let finalArray = [];
this.selectedStop.categories.forEach(
(cat, catIndex) => {
let selected = cat.items.filter((item) => item.count > 0 );
if (selected.length > 0) {
//here we add the catIndex and itemIndex to have it calling the rigth shit
selected = selected.map(val => {
let itemIndex = cat.items.findIndex( itemToFind => itemToFind.id === val.id);
return {
...val,
catIndex: catIndex,
itemIndex: itemIndex,
}})
finalArray = finalArray.concat(selected);
}
});
return finalArray;
}
What confuses me the most is that I have almost the same code in another component, and there it's working as expected, and although the model is changed, the computed property is only recalculated on the first item,
After reading this gist and taking a look again at the posts describing this kind of issue, I decided to give it a try and just make a copy of the whole stored object not just the property, update it, then set it back on vuex using Vue.set, and that did the trick, everything is now working as expected, this is my final store method.
ADD_ITEM_TO_SELECTED_STOP(state, payload) {
let selectedLocalStop = JSON.parse(JSON.stringify(state.selectedStop));
let count = selectedLocalStop.categories[payload.catIndex].items[payload.itemIndex].count;
selectedLocalStop.categories[payload.catIndex].items[payload.itemIndex].count = count + 1;
Vue.set(state,"selectedStop", selectedLocalStop );
//Now we search for this step on the main list
const stepIndex = state.stops.findIndex(val => val.id === selectedLocalStop.id);
Vue.set(state.stops,stepIndex, selectedLocalStop );
},
I had to add the last bit after updating the whole object, because, originally, the array items were updated when the selected item was changed, I guess some sort of reference, but with the object creation, that relationship no longer works "automatic" so I need to update the array by hand

Is there a way to access the current view's data in material-table?

My use case is that when a user filters the table data using search, I'd like to be able to use an external widget to perform actions on each row of that data as it is shown in the table.
Right now I dump all my data into cols={MyData} and sort through data[index] but ideally I'd like to be perform operations with something like currentlyDisplayedTableData[index].
There doesn't seem to be a documented way of doing this so I have no attempt to show, I'm just wondering if someone may have encountered this problem and could show me the light.
re: https://github.com/mbrn/material-table/issues/1124
Just thought I should share another tip, if you just want to intercept/intervene and operate on the currently displayed data before render you can override the component for the table body as Tyler showed in the "issue" link.
But instead of adding a render method, like Tyler did, you can just intercept the props on it's "way down" like this and inject it in the next component (Body, Row, etc.
Note; look for EditRow and other components in https://material-table.com/#/docs/features/component-overriding
<MaterialTable
//...
/**
* be aware when making changes on data that there is a tableData object attached
* rowData: {
* name: 'some name',
* tableData : {id: 3}
* }
*/
components={{
Body: (props) => {
//intervene before rendering table
console.log("tampering with some table data ", props);
console.log(" -- table data looks like this ", props.renderData);
// do stuff..
const myRenderData = props.renderData;
return (
<>
<MTableBody {...props} renderData={myRenderData} />
{/* to show that you will make impact */}
{/* <MTableBody {...props} renderData={[]} /> */}
</>
)
},
Row: (props) => {
//intervene before rendering row
console.log("tampering with some row data ", props);
console.log(" -- row data looks like this ", props.data);
console.log(" -- row table data looks like this ", props.data.tableData);
// do stuff..
const myRenderData = props.data;
return (
<>
<MTableBodyRow {...props} data={myRenderData} />
</>
)
}
}}
#imjared
I found this thread, via the issue, today and have now worked on and tested two working solutions for how to get hold on the filtered data. Maybe thisos what you want, or at least can hint you where to go, so I thought I should share it =)
Option 1 - listen for changes in MaterialTable.state.data with reference. (useRef, and UseEffect)
Option 2 - built in MaterialTable.onSearchChange combined with reference to MaterialTable.state.data
note, I have included 2 flavors of option 2.
Thanks #tylercaceres for the example you provided, it didn't fit for me but gave me a hint on how to do it.
Code is found here: MaterialTableGettingHoldOfRenderData.js
material-table example getting filtered data, the tables current view data, including 2 options and some other examples of actions/buttons, how to use SvgIcon from Material-UI

Filling form fields of a webpage opened in a web view with React Native

I'm working on a project that includes a module that helps with electricity recharge, so what happens is that the user's data is already saved in the app and when they choose to recharge, the app opens up this webpage in a web view.
Currently, I'm using WebBridgeView for opening the webpage as:-
render() {
return (
<WebViewBridge
ref="webviewbridge"
onBridgeMessage={this.onBridgeMessage.bind(this)}
source={{uri: "https://currencypin.com/PrepaidMeterPaymentsV2.0/cartwiz?c=IN&p=5&pm=tm"}}/>
);
}
}
Now, what I want is that when the webpage opens, the form fields come prefilled with the custom data that I have. So that the only field that the user needs to fill on the page is the CAPTCHA.
I was following this article for achieving the same, but it actually assumes that the website is customizable. Which is not possible in my case because it belongs to a 3rd party vendor.
What are the ways to achieve this?
You have to use the injectedJavaScript prop from WebView.
First declare a jsCode variable:
const amount = 2
const jscode = `
if (document.getElementById('txtAmount') == null) {
// field not existing, deal with the error
} else {
document.getElementById('txtAmount').value = '${amount}';
}
`
Please notice the " ` " character. Used to put variables in strings.
Then use it like so:
<WebViewBridge
ref="webviewbridge"
onBridgeMessage={this.onBridgeMessage.bind(this)}
injectedJavaScript={jsCode}

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.

React-Native + Redux: Random number of form fields

I am a newbie to react-native, redux and saga and have run into a use case that I have not been able to find a solution for. I understand how to map state to properties and pass around the state between action, reducer and saga. This makes sense to me so far. This is where things seem to get dicey. I have a form that requires a variable number of form fields at any given time depending upon what is returned from the database.
As an example, let's say I have a structure like this:
{
name: ‘’,
vehicleMake: ‘’,
vehicleModel: ‘’,
carLotCity: ‘’,
carLotState: ‘’,
carLotZipCode: ‘’,
localPartsManufacturers: [{name: ‘’, address: ‘’, zipCode}]
}
Everything from name to carLotZipCode would only require one text field, however, the localPartsManufacturers array could represent any number of object that each would need their own set of text fields per each object. How would I account for this with redux as far as mapping the fields to the state and mapping the state to the properties? I am confused about how to begin with this scenario. I understand how to project mapping when the fields are fixed.
I would keep the data as it is coming from the backend. That way you'll avoid normalizing it. I think we just have to be smarter when rendering the fields. Here's what I'm suggesting:
function onTextFieldChange(name, index) {
// either name = `name`, `vehicleMake`, ...
// or
// name = `localPartsManufacturers` and `index` = 0
}
function createTextField(name, index) {
return <input
type='text'
name={ name }
onChange={ () => onTextFieldChange(name, index) } />;
}
function Form({ fields }) {
return (
<div>
{
Object.keys(fields).reduce((allFields, fieldName) => {
const field = fields[fieldName];
if (Array.isArray(field)) {
allFields = allFields.concat(field.map(createTextField));
} else {
allFields.push(createTextField(fieldName));
}
return allFields;
}, [])
}
</div>
);
}
Form receives all the data as you have it in the store. Then we check if the field is an array. If it is an array we loop over the fields inside and generate inputs same as the other properties createTextField. The tricky part here is how to update the data in the store. Notice that we are passing an index when the text field data is changed. In the reducer we have to write something like:
case FIELD_UPDATED:
const { name, index, value } = event.payload;
if (typeof index !== 'undefined') {
state[name][index] = value;
} else {
state[name] = value;
}
return state;
There is nothing preventing you from keeping a list, map, set or any other object in Redux.
The only thing remaining then, is how you map the state to your props, and how you use them. Instead of mapping a single element from the collection to a prop, you map the entire collection to a single prop, and then iterate over the collection in your render method.
In the action you can pass a new collection back, which is comprised of the form fields making up the parts list. Then, your reducer will replace the collection itself.
Or, upon changing an element in the part collection, you can send an action with its id, find it in the collection in the reducer and replace the element that was changed / add the new one / remove the deleted one.