React: Cannot replace the component inside render() method - reactjs

I am building a simple question-answer game for practise, however having some issues on question change. The way I planned to handle the rendering questions one by one is to create a queue of questions inside an array as component state (PractiseScreen.prototype.state.questionQueue) and using an IntermediateRenderer component to pass the current question index as props and the question queue as it is, so it could render the correct question on parent component's state change. However, when I debug the component, I do not see that the next question is rendered.
What is the issue in my code? Thanks in advance!
import React from 'react';
import * as Animatable from 'react-native-animatable';
import {
View,
Text,
StatusBar,
TouchableWithoutFeedback,
} from 'react-native';
import MyText from '../custom-components/MyText';
import Question from '../components/Question';
import entries from '../mockData/entries';
class IntermediateRenderer extends React.Component {
render() {
return (<React.Fragment>
{this.props.allQuestionComponents[this.props.questionIndexToRenderNext]}
</React.Fragment>)
}
}
export default class PractiseScreen extends React.Component {
static navigationOptions = {
header: null
}
constructor() {
super();
this.state = {
currentQuestionIndex: 0,
questionQueue: []
}
this.buildQuestionQueue = this.buildQuestionQueue.bind(this);
this.goToNextQuestion = this.goToNextQuestion.bind(this);
}
componentDidMount() {
this.buildQuestionQueue();
}
buildQuestionQueue() {
const questionQueue = [];
entries.forEach(entry => {
questionQueue.push(<Question
entry={entry}
goToNextQuestion={this.goToNextQuestion}
/>)
});
this.setState({ questionQueue });
}
goToNextQuestion() {
console.log('Go to next question request is received.');
this.setState({ currentQuestionIndex: this.state.currentQuestionIndex + 1 }, () => {
console.log('Current question index is now: ', this.state.currentQuestionIndex);
})
}
render() {
if(this.state.questionQueue !== 0) {
return <React.Fragment>
<IntermediateRenderer
allQuestionComponents={this.state.questionQueue}
questionIndexToRenderNext={this.state.currentQuestionIndex}
/>
</React.Fragment>
} else {
<View>
<Text>
Questions are loading.
</Text>
</View>
}
}
}

Well, I think you just forgot return statement:
render() {
if(this.state.questionQueue !== 0) {
return <React.Fragment>
<IntermediateRenderer
allQuestionComponents={this.state.questionQueue}
questionIndexToRenderNext={this.state.currentQuestionIndex}
/>
</React.Fragment>
} else {
return <View>
<Text>
Questions are loading.
</Text>
</View>;
}
}
It seems that it runs fine first time, but when you proceed with next question it fails on missing return statement and display nothing.

Related

how to pass state using navigator in react native navigator

I want to pass a state using react navigator. I want to pass showing: false, so my progress bar component will disappear.Can someone please explain how I can do this. Thanks so much.
Here is my code.
import React, { Component } from "react";
import { Button, View, Text, TextInput } from "react-native";
import ContinueButton from "./ContinueButton";
import { CreateAboutMe } from "./StyleSheet/AboutMeStyle";
import * as Progress from "react-native-progress";
export class AboutUser extends Component {
constructor(props) {
super(props);
this.navigatToInterests = this.navigatToInterests.bind(this);
this.checkEntry = this.checkEntry.bind(this);
this.state = {
value: "",
showing: true,
};
}
navigatToInterests = ({ navigation }) => {
let checkDescription = this.state.value;
if (checkDescription === "") {
alert("Please tell people about yourself");
} else {
this.props.navigation.navigate("Interests");
}
};
checkEntry = (Description, value) => {
this.setState({ value: value });
console.log(this.state.value);
};
render() {
return (
<View style={CreateAboutMe.overAllContainer}>
{this.state.showing && (
<Progress.Bar
progress={0.7667}
width={300}
color={"red"}
style={CreateAboutMe.progressbar}
showing={this.state.showing}
/>
)}
Which version of React Navigation are you using?
In version 4, you can send some data using the second argument of the navigate function like this:
this.props.navigation.navigate("Interests",{"someKey":"someValue", ...});
Then you can grab the data in the next page through the props:
let someValue = this.props.navigation.getParam('someKey');

Not sure if i'm using react context correcly

I've created a form in react and after some research i think that if you don't want to use an external library to manage the form, the context could be the best choice, expecially in my case where i've many nested component that compose it.
But, i'm not sure that putting a function inside my state is a good thing.
But let me give you some code:
configuration-context.js
import React from 'react'
export const ConfigurationContext = React.createContext();
ConfigurationPanel.jsx
import React, { Component } from 'react'
import { Header, Menu, Grid } from 'semantic-ui-react'
import ConfigurationSection from './ConfigurationSection.jsx'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationPanel extends Component {
constructor(props) {
super(props)
this.state = {
activeItem: '',
configuration: {
/* the configuration values */
banana: (data) => /* set the configuration values with the passed data */
}
}
}
handleItemClick = (e, { name }) => this.setState({ activeItem: name })
render() {
return (
<ConfigurationContext.Provider value={this.state.configuration}>
<Grid.Row centered style={{marginTop:'10vh'}}>
<Grid.Column width={15} >
<div className='configuration-panel'>
/* SOME BUGGED CODE */
<div className='configuration-section-group'>
{this.props.data.map((section, i) => <ConfigurationSection key={i} {...section} />)}
</div>
</div>
</Grid.Column>
</Grid.Row>
</ConfigurationContext.Provider>
)
}
}
ConfigurationItem.jsx
import React, { Component } from 'react'
import { Input, Dropdown, Radio } from 'semantic-ui-react'
import {ConfigurationContext} from './configuration-context.js'
class ConfigurationItem extends Component {
static contextType = ConfigurationContext
constructor(props) {
super(props)
}
handleChange = (e, data) => this.context.banana(data)
itemFromType = (item) =>{
switch (item.type) {
case "toggle":
return <div className='device-configuration-toggle-container'>
<label>{item.label}</label>
<Radio name={item.name} toggle className='device-configuration-toggle'onChange={this.handleChange} />
</div>
/* MORE BUGGED CODE BUT NOT INTERESTING*/
}
}
render() {
return this.itemFromType(this.props.item)
}
}
So, at the end i've a ConfigurationContext that is just a declaration, everything is inside the parent state.
The thing that i don't like is putting the banana function inside the state (it will have more logic that just logging it)
What do you think about it?
Any suggestion is appreciated.
Thanks
banana is just a regular function and you do not have to put it in the state, just do:
class ConfigurationPanel extends Component {
banana = data => console.log(data)
...
render() {
return (
<ConfigurationContext.Provider value={{banana}}>
...
}
After that you can use this.context.banana(data) as normal.

Why would a react-native view not update after render has successfully run?

I have a component DeckListView which I navigate to after updating state with redux. When I use the debugger in chrome I can see the this.props.Decks.map((deck) loop going through successfully with a list of data, but when I see the screen I don't see the additional Text. Any idea what may be happening?
I have what I believe to be the key code snippets below. The rest can be found at https://github.com/wcwcaseman/mobile-flashcards
Reducer
case ADD_DECK :
return {
...state,
[action.deck.title]:action.deck,
}
Navigation
homePage = () => {
this.props.navigation.navigate('DeckListView');
}
Actual page
import React, { Component } from 'react';
import { View, Text} from 'react-native';
import { connect } from 'react-redux'
class DeckListView extends Component {
render() {
return (
<View>
<Text>Toast the world</Text>
{this.props.Decks.map((deck) => {
<Text key={deck} >item</Text>
})}
</View>
);
}
}
function mapStateToProps ({ decks }) {
let Decks = [];
if(decks !== {} && decks !== null)
{
Decks = Object.keys(decks);
}
return {
Decks: Decks
}
}
export default connect(mapStateToProps)(DeckListView)
You need to return from the map function
{this.props.Decks.map((deck) => {
return <Text key={deck} >item</Text>
})}

React native set state is not working

I am trying to update state in react native component.
But its getting errors, Could someone help me.
I'm using react-native-cli verions: 2.0.1
react-native verions: 0.55.4
Here is my code:
import React, { Component } from 'react'
import {
Button,
Text,
View,
} from 'react-native';
export class ToggleButton extends Component {
state = {
isDone: false
};
onAction() {
const value = !this.state.isDone;
this.setState({ isDone: value });
const newValue = this.state.isDone;
console.log(newValue);
}
render() {
return (
<View>
<Button
title="Action"
onPress={this.onAction}
/>
</View>
)
}
}
export default ToggleButton;
You have three different solutions.
Bind your function in the constructor.
Use the experimental public class fields syntax.
Pass a lambda to executing your function.
The problem is that you're loosing the reference to this, because the function is not executed in the original context, so this.setState is not a function, but a undefined.
In this page there are examples for all of the approaches: https://reactjs.org/docs/handling-events.html
Change
onPress={this.onAction}
to
onPress={this.onAction.bind(this)}
Check: this
Below is the solution
import React, { Component } from 'react'
import {
Button,
Text,
View,
} from 'react-native';
export class ToggleButton extends Component {
// Add state in constructor like this
constructor(props){
super(props);
this.state = {
isDone: false
};
}
onAction() {
const value = !this.state.isDone;
this.setState({ isDone: value });
const newValue = this.state.isDone;
console.log(newValue);
}
render() {
return (
<View>
<Button
title="Action"
// Add function with onPress
onPress={() => this.onAction}
/>
</View>
)
}
}
export default ToggleButton;

React Native: this.state of reusable components not expected

I'm a newbie of React native and honestly I have just a very basic knowledge of React. I'm developing a sample application in which I make use of reusable components and ES6 sintax.
I'am experiencing unexpected results when reusing the same component multiple times in the same Scene (I also make use of Navigator). More precisely I can't understand why differents components (of the same type) are apparently conditions each others states.
I'm posting my code for a better understanding.
This is my main page , in which I make use 2 times of the same custom defined component < TopCategories /> :
HomeScene.js
import React from 'react';
import {View} from 'react-native';
import BaseScene from './BaseScene'
import SearchSuggest from '../components/SearchSuggest';
import TopCategories from '../components/TopCategories'
import styles from '../styles'
export default class HomeScene extends BaseScene {
render() {
return(
<View style={styles.container}>
<SearchSuggest
navigator={this.props.navigator}
/>
<TopCategories/> //first
<TopCategories/> //second
</View>
)
}
}
These are the details of the inner components used:
TopCategories.js
import React, { Component } from 'react';
import {
Text,
View,
StyleSheet
} from 'react-native';
import styles from '../styles'
import utility from '../utility'
import serverConnector from '../serverConnector'
import routes from '../routes'
import MenuItemComplex from './MenuItemComplex'
export default class TopCategories extends Component {
constructor(props) {
super(props);
this.state = {categories: []};
this._fetchContent();
}
_updateCategoriesList(rawCategories){
//store in state.categories array a simplied version of
//the contents received from the server
let simplifiedCategories = [];
for(i=0; i<rawCategories.length; i++){
var simpleCat = {};
simpleCat.id = rawCategories[i].uniqueID;
simpleCat.name = rawCategories[i].name;
simplifiedCategories.push(simpleCat);
}
this.setState({categories: simplifiedCategories });
}
_fetchContent(){
//fetch content from server in JSON format
_topCategories = this;
serverConnector.call(
"CATEGORY",
"FindTopCategories",
{},
function(err, json){
if(err!=null) utility.log("e", err);
else {
try{
_topCategories._updateCategoriesList(json.res.header.body.CatalogGroupView);
}catch(err){
utility.log("e", err);
}
}
}
)
}
openCategoryScene(id, name){
//push on Navigator stack the next route with additional data
let nextRoute = routes.get("categoriesListFirst");
nextRoute.passProps = {
categoryId: id,
categoryName: name
};
this.props.navigate(nextRoute)
}
render(){
console.log(this.state)
return (
<MenuItemComplex key="categories" name="Catalogo" icon="list-b" onItemSelected={this.openCategoryScene.bind(this)} subItems={this.state.categories} />
)
}
}
and finally
MenuItemComplex.js
import React, { Component } from 'react';
import { View, Text, Image, TouchableHighlight, TouchableWithoutFeedback } from 'react-native';
import styles from '../styles'
export default class MenuItemComplex extends Component{
static propTypes = {
name : React.PropTypes.string.isRequired,
icon : React.PropTypes.string.isRequired,
subItems: React.PropTypes.array.isRequired,
onItemSelected: React.PropTypes.func.isRequired
};
render(){
let subItems = [];
for(i=0; i<this.props.subItems.length; i++){
let subItem = this.props.subItems[i];
subItems.push(
<TouchableHighlight
key={subItem.id}
underlayColor={"#d00"}
activeOpacity={1}
onPress={() => this.props.onItemSelected(subItem.id, subItem.name)}
>
<View style={styles.menuSubItem}>
<Text style={[styles.mmText, styles.menuSubItemText]} >
{subItem.name}
</Text>
</View>
</TouchableHighlight>
)
}
return(
<View>
<TouchableWithoutFeedback disabled={true}>
<View style={styles.menuItem}>
<Image style={styles.menuItemImage} source={{uri: this.props.icon}} />
<Text style={[styles.mmTextBold, styles.menuItemText]}>{this.props.name}</Text>
</View>
</TouchableWithoutFeedback>
{subItems}
</View>
)
}
}
I can't understand why the state.simplifiedCategories of the first < TopCategories > component used in my HomeScene seems to be an empty array after the second < TopCategories > component has rendered. So far I thought that the two components were completely isolated, with their own "private" state. But in this case seems that this is shared somehow.
Can someone explain what is happening here ? And then how can I fix that ?
Thanks
EDIT 2016/09/05
As suggested by user V-SHY I tried to give every component a randomic string as key, but this does not solve the problem.
What I find very strange is that I can see only an instance of < TopCategories > in the global window object, the last one.
The screenshot here refers to a test made with
<TopCategories key="tc_first" {...this.props}/>
<TopCategories key="tc_second" {...this.props}/>
in the HomeScene.js file
As suggested by Daniel there was an issue during the fetch of data from server. In particular I was wrong when creating a _topCategories=this object inside TopCategories.js file
_fetchContent(){
_topCategories = this; // ISSUE HERE!
serverConnector.call(
"CATEGORY",
"FindTopCategories",
{},
function(err, json){
if(err!=null) utility.log("e", err);
else {
try{
_topCategories._updateCategoriesList(json.res.header.body.CatalogGroupView);
}catch(err){
utility.log("e", err);
}
}
}
)
}
I solved passing the fetchContent method a reference of the component :
constructor(props) {
super(props);
this.state = {categories: []};
this._fetchContent(this); // passing THIS reference
}
_updateCategoriesList(rawCategories){
let simplifiedCategories = [];
for(i=0; i<rawCategories.length; i++){
var simpleCat = {};
simpleCat.id = rawCategories[i].uniqueID;
simpleCat.name = rawCategories[i].name;
simplifiedCategories.push(simpleCat);
}
this.setState({categories: simplifiedCategories});
}
_fetchContent(instanceRef){
motifConnector.call(
"CATEGORY",
"FindTopCategories",
{},
function(err, json){
if(err!=null) utility.log("e", err);
else {
try{
instanceRef._updateCategoriesList(json.res.header.body.CatalogGroupView);
}catch(err){
utility.log("e", err);
}
}
}
)
}

Resources