React-Native - View config not found for name "Custom Tag" - react-native

I am using react-native-navigation v2 and every component needs to be registered into the navigation by calling registerComponent(). However, I found myself having 500 lines of code where I register every component of my app using the same registerComponent structure with the only difference of using different jsx tag for every component I register, like so:
import ItemsList from './src/components/ItemsList';
import { Navigation } from "react-native-navigation";
import { Provider } from "react-redux";
import reduxStore from "./src/store";
Navigation.registerComponent(
"app.ItemsList",
() => props => (
<Provider store={reduxStore}>
<ItemsList {...props} />
</Provider>
),
() => ItemsList
);
+++ 35 more components almost exactly just like this one
Now, in order to reduce that huge amount of identical code, I've decided to write an IIFE that maps through an array of objects(components) that look like:
[...,
{
name: "ItemsList",
component: ItemsList
},
...]
then calls registerComponent on every item and returns the JSX I need, like so:
(function componentsRegistration() {
return components.map(({ name, component }) => {
const Tag = name;
Navigation.registerComponent(
`app.${name}`,
() => props => (
<Provider store={reduxStore}>
<Tag {...props} />
</Provider>
),
() => component
);
});
})()
After this specific manipulation, my app doesn't render anymore. Instead it throws the "Invariant Violation: View config is not found for name ItemsList". I think I've made all of this respecting the React commandments (capital letter jsx tag, etc.), however can't get it to work. If anyone could, please help.

[SOLVED] I was getting an error because I was trying to pass a string with the component name instead of the component itself.
The right way to do it would be:
const Tag = component;
instead of:
const Tag = name;

Related

what is the difference between passing an object with a variable and a lone variable to a react native component?

My lack of success in this problem may be due to a lack of proper terminology when Googling it but nonetheless I am completely stumped. I am passing an onPress function to a custom component in react native. When I pass it by itself as:
export const AddMorePlants = ( onPress ) => {
return (
<TouchableHighlight
onPress={onPress}>
.
.
.
}
I get a this2.props.onPress is not a function error but when I have the exact same code except with the onPress passed within curly braces:
export const AddMorePlants = ({ onPress }) => {
return (
<TouchableHighlight
onPress={onPress}>
.
.
.
}
It Works!
Why does the second one work and not the first?
Sorry for a kind of basic question I just have been really Googling and cant figure it out. Thanks in advance and I can provide any more info if needed.
A functional component in React only has one parameter. The props. You can read more about it here
So what your first attempt at passing the onPress function actually looks like is:
export const AddMorePlants = (props) => {
return (
<TouchableHighlight onPress={props}/>
);
}
When the TouchableOpacity tries to execute the method, it hits the is not a function error because props is an object.
When you do:
export const AddMorePlants = ({onPress}) => {
return (
<TouchableHighlight onPress={onPress}/>
);
}
what you are doing is something called destructuring assignment and it's equivalent of doing:
export const AddMorePlants = (props) => {
const {onPress} = props;
return (
<TouchableHighlight onPress={onPress}/>
);
}
By putting the brackets inside the parentheses you are just doing a shorthand version of this destructuring assignment that we have mentioned.
Here's another version that would also work:
export const AddMorePlants = (props) => {
return (
<TouchableHighlight onPress={props.onPress}/>
);
}
As you can see there are many ways to access an object's property.
The important part is to remember that the props object is the only parameter passed into a functional component.
I hope this helps you understand what's going on there.
In the first function you need to pass only one param i.e onPress to your component while in second you are destructuring assignment so you are doing something like onPress = this.props.onPress and passing an object of params.

Using FlatList#onViewableItemsChanged to call a Component function

I'm currently attempting to implement a form of LazyLoading using the FlatList component, which introduces a neat little feature called onViewableItemsChanged which gives you a list of all of the components that are no longer on the screen as well as items that are now on the screen.
This is a custom LazyLoad implementation and as such is more complicated than most LazyLoad open-sourced libraries that are available, which is why I'm working on my own implementation. I'm already looked into react-native-lazy-load and others.
Basically, I need to be able to call a function that's part of the component being rendered in the FlatList, I've tried creating a reference to the item rendered in the FlatList and calling it as such, but it doesn't seem to work.
For example:
<FlatList data={...}
renderItem={(item) => <Example ref={(ref) => this[`swiperRef_${item.key}`] = ref}}
onViewableItemsChanged={this.onViewableItemsChanged}
/>
onViewableItemsChanged = ({viewableItems}) => {
viewableItems.forEach((item) => {
const { isViewable, key } = item;
if(isViewable && !this.cachedKeys.includes(key)) {
const ref = this[`swiperRef_${key}`];
if(!ref) return console.error('Ref not found');
ref.startLoading();
this.cachedKeys.push(key);
}
});
}
Now in the <Example /> component I would have a function called startLoading which should be called when a new visible item is brought onto the screen, however the ref never exists.
I was actually doing everything correctly, but I accidently forgot to deconstruct the parameter returned from the renderItem function, so (item) should have been ({ item })
That's all there was to it.

Import component twice

Can't find a good example of how to do this, but I have a component that I would like to use twice in a component.
Right now I just import by:
import Picker from 'react-native-picker';
Which for this particular component only imports 1 instance of the Picker object. Is there a way to important two independent objects.. such as
import Picker as picker1, picker2 from 'react-native-picker';
Is there something out there like this?
----EDIT:
Here is the example code of how to use this particular component. I was trying to keep it specific as I know the community here likes it better when it is more general to benefit more people...thanks!
import Picker from 'react-native-picker';
let data = [];
for(var i=0;i<100;i++){
data.push(i);
}
Picker.init({
pickerData: data,
selectedValue: [59],
onPickerConfirm: data => {
console.log(data);
},
onPickerCancel: data => {
console.log(data);
},
onPickerSelect: data => {
console.log(data);
}
});
Picker.show();
You would probably have to create two different components in different files and then import Picker1 and Picker2 to the parent component.
You can either do everything you need inside these components or give them a ref prop in the parent and you'll have access to their methods.
import Picker1 from './picker1'
import Picker2 from './picker2'
class ParentComponent extends Component {
render() {
return (
<View>
<Picker1 ref={(picker1) => this.picker1 = picker1} />
<Picker2 ref={(picker2) => this.picker2 = picker2} />
</View>
)
}
}

How to load local files in react native based on id in component?

I need to load an item in one component, based on its id which came from props.
I tried to use fetch, but it can't load local file.
Also tried import from, but it has to be in the beginning of file, not in component.
I want something like this:
const Detail = (props) => {
let id = props.data;
let path = './book/'+id+'.json';
var bookData = require(path);
return (
<Container>
<Header></Header>
<Content>
<Text>ID: {id}</Text>
<Text>{bookData}</Text>
</Content>
</Container>
);
}
Never tried anything like this but try changing your return to
return ({ ID: {id, ...bookData} })

Spying on React components using Enzyme (and sinon?) to check arguments

I'm wanting to assert that a component gets called from within another component with the correct arguments.
So within the component that I am testing there is a Title component that gets called with properties title & url. I'm trying to assert that it gets called with the correct arguments.
I'm pretty sure I want to use a sinon spy and do something like this
const titleSpy = sinon.spy(Title, render)
expect(titleSpy).to.be.calledWith( '< some title >' )
but with regards to React and Enzyme, I'm not really sure what I should be spying on. (Because apparently it's not render!)
In my spec file I am importing Title and console.loging it's value to find a function to spy on and I get:
function _class() {
_classCallCheck(this, _class);
return _possibleConstructorReturn(this, Object.getPrototypeOf(_class).apply(this, arguments));
}
Any ideas on how I can do this? Is it a case of going through and finding the element and checking it's attributes? If so that seems a bit...messy and seems like it goes against the principle of the Shallow render ("Shallow rendering is useful to constrain yourself to testing a component as a unit").
If you're just checking the value of properties passed to the component, you don't need sinon. For example, given the following component:
export default class MyComponent extends React.Component {
render() {
return (
<MyComponent myProp={this.props.myProp} />)
}
}
Your test might look like this:
describe('MyComponent ->', () => {
const props = {
myProp: 'myProp'
}
it('should set myProp from props', () => {
const component = shallow(<MyComponent {...props} />)
expect(component.props().myProp).to.equal(props.myProp)
})
})
You can achieve it with the help of .contains() method, without messing up with spies.
If you have a component:
<Foo>
<Title title="A title" url="http://google.com" />
</Foo>
You can make such an assertion:
const wrapper = shallow(<Foo />);
expect(wrapper.contains(<Title title="A title" url="http://google.com" />)).to.equal(true);
Such will fail:
const wrapper = shallow(<Foo />);
expect(wrapper.contains(<Title title="A wrong title" url="http://youtube.com" />)).to.equal(true);
This is an older question, but my approach is a little different than the existing answers:
So within the component that I am testing there is a Title component that gets called with properties title & url. I'm trying to assert that it gets called with the correct arguments.
ie. You're wanting to check that the component being tested renders another component, and passes the correct prop(s) to it.
So if the component being tested looks something like:
const MyComp = ({ title, url }) => (
<div>
<Title title={title} url={url} />
</div>
)
Then the test could look something like:
import Title from 'path/to/Title';, u
it('renders Title correctly', () => {
const testTitle = 'Test title';
const testUrl = 'http://example.com';
const sut = enzyme.shallow(<MyComp title={testTitle} url={testUrl} />);
// Check tested component rendered
expect(sut.exists).toBeTruthy();
// Find the Title component in the subtree
const titleComp = sut.find(Title); // or use a css-style selector string instead of the Title import
// Check that we found exactly one Title component
expect(titleComp).toHaveLength(1);
// Check that the props that were passed were our test values
expect(titleComp.prop('title')).toBe(testTitle);
expect(titleComp.prop('url')).toBe(testUrl);
});
I generally find Enzyme's functions to be very useful for all kinds of checks about components, without needing other libraries. Creating Sinon mocks can be useful to pass as props to components, to (for example) test that a callback prop is called when a button is clicked.