In the ListItem.js file which is the renderRow from ListView I added a Navigator but it's not showing anything in the renderScene
const item = {};
export default class ListItem extends Component{
constructor(props){
super(props);
item = this.props.item;
}
renderScene(route, navigator){
return(
<Text style={{color:'#fff'}}>Test</Text>
<Text style={{color:'#fff'}}>{item.title}</Text>
)
}
render(){
return(
<Navigator
renderScene={this.renderScene}
navigator={this.props.navigator} />
);
}
}
When I put everything in the render() it works fine. If I console.log() my item object in the renderScene it logs the item, but it shows nothing.
I feel like I experienced a similar issue with abstracting renderScene into its own function, almost as if the Navigator can't handle the render. All the docs demo the render being inline. What you could do is offload the render in a separate function, but still call it inline, like this.
render(){
return(
<Navigator
renderScene={(route, navigator) => this.renderScene(route, navigator)}
navigator={this.props.navigator} />
);
}
Since you are using ES6 syntax, there is no autobinding. You can use fat arrow syntax and do something like this:
renderScene = (route, navigator) => {
return(
<View>
<Text style={{color:'#fff'}}>Test</Text>
<Text style={{color:'#fff'}}>{item.title}</Text>
</View>
);
}
Related
I'm a little bit confused about how Context API works
as the docs said about Context Provider:
Accepts a value prop to be passed to consuming components that are descendants of this Provider. One Provider can be connected to many consumers. Providers can be nested to override values deeper within the tree.
I just start to try a simple code like this:
//App.js
export default class App extends React.Component {
constructor(props) {
super(props);
this.changeColor=(color, value)=>{
this.setState({[color]:value})
}
this.state={
changeColor:this.changeColor,
red:0,
green:0,
blue:0,
}
}
render() {
console.disableYellowBox = true;
return (
<ThemeContext.Provider value={this.state}>
<InitialScreen/>
</ThemeContext.Provider>
);
}
}
//InitialScreen.js
export default class Login extends Component {
render() {
return (
<ThemeContext.Consumer>
{({red, green, blue, changeColor})=>(
<View style={{flex:1}}>
<View style={{flexDirection:'row', justifyContent:'space-between'}}>
<Text>Red: </Text>
<Slider
style={{width:'80%'}}
minimumValue={0}
maximumValue={255}
step={1}
value={red}
onValueChange={(e)=>{changeColor('red', e)}}
/>
</View>
<View style={{flexDirection:'row', justifyContent:'space-between'}}>
<Text>Green: </Text>
<Slider
style={{width:'80%'}}
minimumValue={0}
maximumValue={255}
step={1}
value={green}
onValueChange={(e)=>{changeColor('green', e)}}
/>
</View>
<View style={{flexDirection:'row', justifyContent:'space-between'}}>
<Text>Blue: </Text>
<Slider
style={{width:'80%'}}
minimumValue={0}
maximumValue={255}
step={1}
value={blue}
onValueChange={(e)=>{changeColor('blue', e)}}
/>
</View>
<View style={[GlobalStyles.Center, {flex:1, backgroundColor:'rgb('+red+', '+green+', '+blue+')'}]}>
</View>
</View>
)}
</ThemeContext.Consumer>
);
}
}
//themeContext.js
import React from 'react';
export const themes = {
light: {
foreground: '#000000',
background: '#eeeeee',
},
dark: {
foreground: '#ffffff',
background: '#222222',
},
};
export const ThemeContext = React.createContext({
theme: themes.dark,
toggleTheme: () => {},
})
I don't know what's the meaning of Object inside React.createContext({...}), i can pass this.state in Provider value props that's mean I don't need to define anything in React.createContext({...}) AFAIK,
or maybe I'm doing something wrong so I'm out of Context function itself?
You're absolutely right, you don't need to provide anything in React.createContext in order to use it. The data you provided in createContext is merely default value and will be override as you set Context.Provider later on. However, I strongly recommend you to always provide a default value because of the following reasons:
By doing so you can think ahead of what kind of data your context would serve, hence help you to understand the structure of your code better. This habit would help you in long run
It gives whatever IDE you're using (Visual Studio for example) a clue of your Context data and it would give better hints as you code
on clicking the text, i get an error saying "undefined is not an object (evaluating '_this2.categoryClicked.bind')"
I think the error is "onPress={()=>this.categoryClicked.bind(this)}" there must be a different way to call the categoryClicked function when the button is clicked. What is wrong in my code ?
class CategoriesView extends React.Component {
constructor(props){
super(props)
}
categoryClicked(){
this.props.categoryPressed(this.props.Category);
}
renderSubCategory(){
return(
this.props.Category.sub_category.map(function(subCategory, i){
return(
<View style={styles.abcd}>
<TouchableHighlight onPress={()=>this.categoryClicked.bind(this)}>
<Text>{subCategory.title}</Text>
</TouchableHighlight>
</View>
)
})
)
}
render(){
return(
<View style={{flex:1}}>
<View style={styles.avf}>
<Text>{this.props.Category.heading}</Text>
</View>
<View style={styles.ddd}>
{this.renderSubCategory()}
</View>
</View>
)
}
}
I believe what you want to do is onPress={this.categoryClicked.bind(this)} instead of an arrow function. .bind(this) returns a function with the context correctly binded to this, therefore, it does not get invoked.
Also, I suggest putting the binding in constructor, as you don't want the binding to happen every time the component re-renders.
e.g.
constructor(props) {
super(props);
this.categoryClicked = this.categoryClicked.bind(this);
}
Then just use onPress={this.categoryClicked}
If you want to pass down sub-category, you can do
constructor(props) {
super(props);
this.subcategoryClicked = props.Category.sub_categories.map(sub_category => this.categoryClicked.bind(this, sub_category));
}
then use like this in render:
this.props.Category.sub_category.map(function(subCategory, i) {
<View style={styles.abcd}>
<TouchableHighlight onPress={this.subcategoryClicked[i]}>
<Text>{subCategory.title}</Text>
</TouchableHighlight>
</View>
P.S, I am not sure if this is a good pattern to follow. Stick to this.categoryClicked(bind, subcategory) if you are not comfortable with doing this. This is one of those things that I am not sure if the optimization is worth it.
this in onPress={()=>this.categoryClicked.bind(this)}> points to sub_category.map function. It should instead point to the class. Can be done this way instead
return (
this.props.Category.sub_category.map((subCategory, i) => { // <--- this way context is preserved
// other lines
onPress={()=>this.categoryClicked.bind(this, subCategory)}>
// other lines
})
);
in categoryClicked method should be accessible
categoryClicked(category){
this.props.categoryPressed(category);
}
I have this structure, where I have a list of MyComponent:
class MyComponent extends Component
{
props: { navigation: Object, data: Object };
ShowScreenB(data: Object){
this.props.navigation.navigate('ScreenB', {data});
}
render()
{
return (
<Menu>
<MenuTrigger> <Text> Menu </Text> </MenuTrigger>
<MenuOptions>
<MenuOption onSelect={() => this.ShowScreenB.bind(this, this.props.data)} text='Show Screen B' />
</MenuOptions>
</Menu>
);
}
}
class MyScreen extends Component
{
render()
{
let renderRow = (row) => { return (<MyComponent data= {row} navigation= {this.props.navigation} /> );}
return (
<View >
<ListView dataSource={this.state.DataSource} renderRow={renderRow.bind(this)}/>
</View>
);
}
}
But ShowScreenB() does not go to the other screen.
I also tried to prepare the navigator in MyScreen class and then pass it as a function to MyComponent. But also does not work:
class MyComponent extends Component
{
props: { OnPress: Function, data: Object };
render()
{
return (
<Menu>
<MenuTrigger> <Text> Menu </Text> </MenuTrigger>
<MenuOptions>
<MenuOption onSelect={() => this.OnPress.bind(this)} text='Show Screen B' />
</MenuOptions>
</Menu>
);
}
}
class MyScreen extends Component
{
ShowScreenB(data: Object){
this.props.navigation.navigate('ScreenB', {data});
}
render()
{
let renderRow = (row) => { return (<MyComponent data= {row} OnPress= {this.ShowScreenB.bind(this, row)} /> );}
return (
<View >
<ListView dataSource={this.state.DataSource} renderRow={renderRow.bind(this)}/>
</View>
);
}
}
What could be the issue?
Edit: Menu is a PopUp Menu.
You never call ShowScreenB().
Right now you are just binding it:
onSelect={() => this.ShowScreenB.bind(this, this.props.data)}
bind doesn't call the function. All it does it bind it to the given context. You need to actually call ShowScreenB() so your navigation code executes. For example:
onSelect={() => { this.ShowScreenB.bind(this); this.ShowScreenB(this.props.data); }}
Edit to answer comment since it won't fit in a comment:
That's because removing ( ) => makes it so all you have is the { } syntax leftover. { } means to evaluate what is inside the brackets. Take a look at what is written in the link in my answer. The return value of bind is:
A copy of the given function with the specified this value and initial arguments.
So the expression { this.ShowScreenB.bind(this) } will evaluate the return value; hence calling the navigation function. What I posted above was simply one example of what you could do. You could just as well write it as onSelect={() => this.ShowScreenB.bind(this)()} and it would work as well. If you're having trouble with this, you should also brush up on how arrow functions work.
Is there any chance to use a component as a global ActivityIndicator which has transparent color and had been created by me on React-Native?
Details:
I use a redux store to update the UI. So I intend to show an ActivityIndicator by updating the store.
I've created an ActivityIndicator component with name ActIndicator.
I have a main App component that contains the app.
I have a Root component that wraps the ActIndicator and App components.
The ultimate code of render method of Root component looks like the following:
render() {
if (this.state.showActivityIndicator) {
return(
<ActIndicator>
<App />
</ActIndicator>
)
}
return (</App>)
}
I've tried several methods but I can not be successful.
I guess my brain is stopped.
I also guess there may be a logic mistake.
const withLoadingOverlay = (Component) => class withLoadingOverlayView extends React.Component { props: withLoadingOverlayProps
// Indicator view styles loadingOverlay = (style) => (
<View style={[style, styles.someDefaultStyles]}>
<ActivityIndicator color={someColor} size="large" />
</View> )
render() {
const { pending, ...passProps } = this.props;
const { width, height } = Dimensions.get('window');
return (
<View style={{ flex: 1 }}>
<Component {...passProps} />
{pending && this.loadingOverlay({ width, height })}
</View>
); } };
I used to wrap whole container with HOC and with redux action to set on start pending prop true and on success or fail to set on false so this prop will be consumed by HOC and indicator will be displayed only when pending is set on true.
In container you have to wrap component in connect
export default connect(
(state) => ({
pending: state.reducer.pending, // pending prop should be here
}),
(dispatch) => ({ dispatching redux actions })
)(withLoadingOverlay(WrappedComponent));
I don't think you're supposed to pass App as a child, the way I use it is more like this:
render() {
if (this.state.showActivityIndicator) {
return(
<View>
<ActivityIndicator />
<App />
</View>
)
}
return (<App />)
}
but it would probably be better to set it up like this:
render() {
return (
<View>
<ActivityIndicator animating={this.state.showActivityIndicator} />
<App />
</View>
)
}
So I am using react-native-router-flux for my navigation in my ReactJS app but I am having difficulties using it with components.
For example the following code snippet works perfectly by pushing the next page when clicking the links.
export default class MainMenuPage extends Component {
render() {
return (
<View>
<Text>Main Menu</Text>
<TouchableOpacity onPress={Actions.bookmarksPage}>
<Text>Bookmarks</Text>
</TouchableOpacity>
<TouchableOpacity onPress={Actions.settingsPage}>
<Text>Settings</Text>
</TouchableOpacity>
</View>
);
}
}
While the following code snippet nothing happens when you press the links.
export default class MainMenuPage extends Component {
render() {
return (
<View>
<Text>Main Menu</Text>
<MenuButton name="Bookmarks" onPress={Actions.bookmarksPage} />
<MenuButton name="Settings" onPress={Actions.settingsPage} />
</View>
);
}
}
class MenuButton extends Component {
render() {
return(
<TouchableOpacity>
<Text>{this.props.name}</Text>
</TouchableOpacity>
);
}
}
Obviously the second code snippet is much more cleaner.
You have to pass the onPress prop through to the TouchableOpacity:
class MenuButton extends Component {
render() {
return(
<TouchableOpacity onPress={this.props.onPress}>
<Text>{this.props.name}</Text>
</TouchableOpacity>
);
}
}