React Native, Navigating a prop from one component to another - json

handleShowMatchFacts = id => {
// console.log('match', id)
return fetch(`http://api.football-api.com/2.0/matches/${id}?Authorization=565ec012251f932ea4000001fa542ae9d994470e73fdb314a8a56d76`)
.then(res => {
// console.log('match facts', matchFacts)
this.props.navigator.push({
title: 'Match',
component: MatchPage,
passProps: {matchInfo: res}
})
// console.log(res)
})
}
I have this function above, that i want to send matchInfo to matchPage.
I take in that prop as follows below.
'use strict'
import React from 'react'
import { StyleSheet, View, Component, Text, TabBarIOS } from 'react-native'
import Welcome from './welcome.js'
import More from './more.js'
export default class MatchPage extends React.Component {
constructor(props) {
super(props);
}
componentWillMount(){
console.log('mathc facts ' + this.props.matchInfo._bodyInit)
}
render(){
return (
<View>
</View>
)
}
}
All the info I need is in that object - 'this.props.matchInfo._bodyInit'. My problem is that after '._bodyInt', I'm not sure what to put after that. I've tried .id, .venue, and .events, they all console logged as undefined...

You never change props directly in React. You must always change the state via setState and pass state to components as props. This allows React to manage state for you rather than calling things manually.
In the result of your api call, set the component state:
this.setState({
title: 'Match',
component: MatchPage,
matchInfo: res
}
Then pass the state as needed into child components.
render() {
return(
<FooComponent title={this.state.title} matchInfo={this.state.matchInfo} />
);
}
These can then be referenced in the child component as props:
class FooComponent extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
console.log(this.props.title);
console.log(this.props.matchInfo);
// Etc.
}
}
If you need to reference these values inside the component itself, reference state rather than props.
this.state.title;
this.state.matchInfo;
Remember components manage their own state and pass that state as props to children as needed.

assuming you are receiving json object as response , you would need to parse the response before fetching the values.
var resp = JSON.parse(matchInfo);
body = resp['_bodyInit'];

Related

Get JSON Data in multiple components using reactjs and redux

I would like to show data from a single API to different components as I want to hit the API only once and distribute the data to multiple small components. I know I can do this by using redux state but not sure how to do it. Need your help to achieve this. Below is the code done so far.
homepage/index.js
import SlidingBanner from './banner/BannerList';
import Celebslider from './celebrityslider/CelebSlider';
class HomePage extends Component {
render() {
return (
<div>
<SlidingBanner />
<anotherslider />
</div>
);
}
}
export default HomePage;
BannerList.js
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { itemsFetchData } from '../../../actions/items';
class BannerList extends Component {
componentDidMount() {
this.props.fetchData();
}
render() {
let bannerArray = [];
let banner = this.props.items.banner
for (let key in banner) {
bannerArray.push(banner[key]);
return (
<div>
<Slider {...slidersettings}>
{this.props.items.banner.map((item) => (
<div key={item.id}>
<img src={item.image_url} className="img-responsive"/>
</div>
))}
</Slider>
</div>
);
}
if (this.props.hasErrored) {
return <p>Sorry! There was an error loading the items</p>;
}
if (this.props.isLoading) {
return <p>Loading…</p>;
}
return (null);
}
}
BannerList.propTypes = {
fetchData: PropTypes.func.isRequired,
items: PropTypes.object.isRequired,
hasErrored: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired
};
const mapStateToProps = (state) => {
return {
items: state.items,
hasErrored: state.itemsHasErrored,
isLoading: state.itemsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: (url) => dispatch(itemsFetchData(url))
};
};
export default connect(mapStateToProps, mapDispatchToProps)(BannerList);
anotherslider.js
Now in this file, i want to fetch another array of objects or object from the same API.
I tried to mount the API in container component but did not worked, I hope i am doing some mistake. Please correct.
If you want to fetch data in anotherslider.js file you must connect reducer to class/function inside it as well as you are making it in BannerList.js file.
Now before render call componentWillReceiveProps(nextProps) function and you will get your data here.
If you want to call data in both of the sliders, you have 2 ways to handle it.
Make your redux requests in HomePage.js component and bind the data to the other components.
When you get the data on BannerList.js component, your state will be updated. Just add the redux connection to your anotherslider.js component and get data when updated.
const mapStateToProps = (state) => {
return {
items: state.items,
hasErrored: state.itemsHasErrored,
isLoading: state.itemsIsLoading
};
};
export default connect(mapStateToProps, mapDispatchToProps)(HomeList);
Apart from all these options, you can also use react's Context API as Provider/consumer to distribute your data among small components... this will save you passing props to all small components and directly access the value in component using Context.Consumer .. moreover if you do not want to store this state in global redux store, context API will save you from it...

what does "#observer" attribute do?

First thing I learnt about mobx-react is use "#observer" attribute to track values of properties which defined in state class..
this is my sample below;
//#observer cut it off
SingUp.js
import React, {Component} from 'react'
import {observer} from 'mobx-react'
class SignUp extends Component{
constructor(props){
super(props)
}
SaveUser(e){
e.preventDefault();
this.props.appState.user.username = this.username.value;
this.props.appState.user.password = this.password.value;
this.props.appState.postSaveUser();
}
render(){<form onSubmit={()=>this.SaveUser(e)}>...
when I submit the form it "SaveUser()" called and set app state values. you see I dont define "#observer" attribute at top of SignUp class
and here is state class; AppState.js
import { observable, action} from "mobx"
import {user} from './models/user'
class AppState {
#observable user=new user;
constructor() {
}
postSaveUser(){
debugger
var asd = this.user
}
}
the thing is when I check the values in "postSaveUser()" method I see values exactly I set it "SignIn" component, is it weird?
I thought it only track values assigned in any class which defined with "#observer" attribute but although I dont use it I am able to access data?
Using the #observer decorator on a React component class is much like using autorun. The component will re-render when the observables that got de-referenced in the last render are changed. You can still of course change the value of observable data, it is just that your React component will not re-render automatically if you don't use the #observer decorator.
Example (JSBin)
class Store {
#observable data = 'cool';
}
const store = new Store();
setTimeout(() => {
store.data = 'wow';
}, 2000);
#observer
class Observer extends Component {
render() {
return <h1> This component will re-render when {store.data} changes.</h1>;
}
};
class NonObserver extends Component {
render() {
return <h1> This component will NOT re-render when {store.data} changes.</h1>;
}
};
ReactDOM.render(
<div>
<Observer />
<NonObserver />
</div>,
document.getElementById('app')
);

In React.js array value is not passing properly via props?

I have a react app that has two components one Customer and another called Tags. The Customer sends its state's tags value to Tags. As following:
Customer.jsx
import React from "react";
import Tags from "./Tags.jsx";
export default class Customer extends React.Component {
constructor(props) {
super(props);
this.state = {customer:''};
}
componentDidMount(){
const url = `http://localhost:3000/api/customers/${this.props.params.id}`
fetch(url)
.then(res=>res.json())
.then(data=>{
console.log(data);
this.setState({
customer: data
});
})
.catch(error=>{
console.log(error)
});
}
render() {
return (
<div>
Company Name :{this.state.customer.companyName}
Customer Name :{this.state.customer.name}
Tags: <Tags tags={this.state.customer.tags} />
</div>
);
}
}
Tags.jsx
import React from "react";
export default class Tags extends React.Component {
constructor(props) {
super(props);
}
render() {
let tags = this.props.tags.map(tag=>{
return (<span>tag</span>);
});
return (
<div>
{tags}
</div>
);
}
}
When I run the code I get, "TypeError: Cannot read property 'map' of undefined(…)". If I replace below from Tags.jsx
let tags = this.props.tags.map(tag=>{
return (<span>tag</span>);
});
with
console.log(this.props.tags);
The output is an array.
What is happening? I really do not understand. What can I do?
In the constructor of Customer you are defining the state of a customer as a string, not an object. You should change it to reflect the actual customer properties, ie:
this.state = {customer: {name: '', tags: []}};
It's undefined, because of you're making an API call and the data isn't returned yet.
In your Customer component you can check the existance of this.state.customer.tags and if exist - then render the tags.
Somethling like that:
{ this.state.customer.tags ? <Tags tags={this.state.customer.tags} /> : null }

React-Native - Dynamic State from JSON for Switch

Hey there :) I got following issue by adding a filter Modal to my SearchView
I constructed a SearchPage where several events can be listed. This all workes pretty fine. Now i am trying to add filter to my SearchPage. If i set the filter manually it works pretty fine -> Now my issue:
If i try to change the switch value of the Switch, it set´s back to the root because the state for the value is not set
Steps i did explained:
I am trying to open a Modal View where all my filter are listed and where i can set true/false by using a Switch. My idea was to fetch all filter Settings by creating a JSON for it:
module.exports = {
"filter":
{
"track": [
{
"id": 1,
"description": "IoT & Living tomorrow"
},
{
"id": 2,
"description": "Smart & Digital Retail"
},
{
"id": 3,
"description": "Startups, Digital Culture & Collaboration"
}
]
}
}
The JSON above is just for expample - Normally its much larger and has more topics than just track
Now i import the JSON and save it at the var filter. I checked the data is in the right format here -> filter.track -> All my JSON Objects
Now i created a my class with the filter Modal
import React, {Component} from 'react';
import {
ListView,
Modal,
StatusBar,
StyleSheet,
Text,
TouchableOpacity,
View,
Switch
} from 'react-native';
var filter = require('../JSON/filter');
class PopoverFilter extends Component {
constructor(props) {
super();
// ds for the menu entries
var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
eventTracks: ds.cloneWithRows(filter.filter.track)
}
this.show = this.show.bind(this);
}
render() {
return(
<Modal>
<ListView
style={styles.mainView}
renderRow={this.renderMenuEntries.bind(this)}
dataSource={this.state.eventTracks}/>
</Modal>
);
}
renderMenuEntries(entry) {
var switchState = entry.description;
return(
<View style={styles.switchView}>
<Text style={[styleHelper.fonts.titleSize, styles.text]}>{entry.description}</Text>
<Switch onValueChange={(value) => this.switchChanged(switchState, value)}
value={this.state.switchState}/>
</View>
);
}
switchChanged(field, value) {
var obj = {};
obj[field] = value;
this.setState(obj);
}
}
var styles = StyleSheet.create({
});
module.exports = PopoverFilter;
Please ignore the missing Style and also there are more Objects in the Modal but its not important for this case.
Most important is that i try to render the every Switch by the renderMenuEntries method and i give them all entries -> The works just the Switch is not set right. As far as i try to change the value of the switch it is instant go back to its root. And no state is set.
Maybe my solution is not possible and i have to make every state static - but this solution would be very good in case that i could set dynamic filter later without changing the whole code
The scenario you describe is possible. There were a number of issues I encountered with your code:
In renderMenuEntries the value you were assigning to the <Switch /> component was the description of the data item, instead of the expected boolean that the <Switch /> component value expects. Further, this value was also referencing a property of this.state that didn't exist.
The switchChanged function was also just updating the component state using the data item's description
Using your code sample provided I created a new class from scratch named PopoverFilter. Instead of requiring the filter data within this component, it expects the data to come in via a component prop named filterData. This will promote reusability of the component to accept different datasets.
The code is heavily commented to help explain the concepts demonstrated. Here's the PopoverFilter class:
import React from 'react';
import {
ListView,
Modal,
Switch,
Text,
TouchableOpacity,
View
} from 'react-native';
export default class PopoverFilter extends React.Component {
constructor (props) {
super(props);
// bind relevant handlers up front in the constructor
this.renderRow = this.renderRow.bind(this);
this.onPress = this.onPress.bind(this);
// process the incoming filter data to add a 'selected' property
// used to manage the selected state of its companion switch
this._filterData = this.processFilterData(this.props.filterData);
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.state = {
filterDataSource: ds.cloneWithRows(this._filterData)
}
}
processFilterData (filterData) {
// don't mutate the filterData prop coming in
// use map to create a new array and use Object.assign to make
// new object instances with a new property named 'selected' initialized
// with a value of false
return filterData.map((item) => Object.assign({}, item, { selected: false }));
}
switchChanged (rowId, isSelected) {
const index = +rowId; // rowId comes in as a string so coerce to a number
const data = this._filterData;
// don't mutate this._filterData
// instead create a new array and new object instance
this._filterData = [
...data.slice(0, index), // take everything before the target index
Object.assign({}, data[index], { selected: isSelected }), // create a new object instance with updated selected property
...data.slice(index + 1) // take everything after the selected index
];
// update the listview datasource with the new data
this.setState({
filterDataSource: this.state.filterDataSource.cloneWithRows(this._filterData)
});
}
renderRow (item, sectionId, rowId) {
return(
<View>
<Text>{item.description}</Text>
<Switch
onValueChange={(value) => this.switchChanged(rowId, value)}
value={item.selected}
/>
</View>
);
}
// just a test function used to dump the current state of the _filterData
// to the console
onPress () {
console.log('data', this._filterData);
}
render () {
return (
<Modal>
<ListView
renderRow={this.renderRow}
dataSource={this.state.filterDataSource}
/>
<TouchableOpacity onPress={this.onPress}>
<Text>Get Filter Data</Text>
</TouchableOpacity>
</Modal>
);
}
}
Note this PopoverFilter class also renders a button that when pressed will dump out the current state of the data to the console so you can view it's current form.
Here's an example of how to use the component:
import React from 'react';
import {
AppRegistry,
View
} from 'react-native';
import filterData from './filter';
import PopoverFilter from './PopoverFilter';
class MyApp extends React.Component {
render () {
return (
<View>
<PopoverFilter filterData={filterData.filter.track} />
</View>
);
}
}
AppRegistry.registerComponent('MyApp', () => MyApp);

How to access JSON in a React Component?

I have a JSON object from a script tag like so:
<script type="text/json" id="json-data">
{'someData': 'Lorem ipsum...'}
</script>
I would like to be able to pull this information and use it within a React component in my render method.
The issue seems to be that I need to set this to a variable within componentWillMount:
export default MyReactComponent extends Component {
componentWillMount() {
const test = document.getElementById('json-data').innerHTML;
}
render() {
return (
<div>
// This is where I would like to use this data.
</div>
);
}
}
Is this the best way to handle passing this data? If so, how can I access this data within the render method of my component?
Thanks!
Store it in the component's state. The render method should only depend this.state and this.props
At the risk of oversimplifying:
this.props are passed from parent components
this.state is state that is internal to the component
Example
export default MyReactComponent extends Component {
componentDidMount() {
this.setState({
test: JSON.parse(document.getElementById('json-data').innerHTML)
});
}
render() {
return <div>{this.state.test}</div>;
},
getInitialState: function() {
return {test: {}}
}
}