Parent Component
const App = () => (
<View style={{ flex: 1 }}>
<Header headerText={'Albums'} />
<AlbumList />
</View>
);
Scroll View
import React, { Component } from 'react';
import { ScrollView } from 'react-native';
import axios from 'axios';
import AlbumDetail from './AlbumDetail';
class AlbumList extends Component {
state={ albums: [] };
componentWillMount() {
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(response => this.setState({ albums: response.data }));
}
renderAlbums() {
return this.state.albums.map(album =>
<AlbumDetail key={album.title} album={album} />);
}
render() {
console.log(this.state);
return (
<ScrollView >
{this.renderAlbums()}
</ScrollView>
);
}
}
export default AlbumList;
I am trying to make a ScrollView work. These are the snippet. But still when I run the simulator the scroll view is not working and not giving the full lists of the items.
Try this, basically change the render method return
import React, { Component } from 'react';
import { ScrollView } from 'react-native';
import axios from 'axios';
import AlbumDetail from './AlbumDetail';
class AlbumList extends Component {
this.state={ albums: [] };
componentDidMount() {
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(response => this.setState({ albums: response.data }));
}
renderAlbums() {
return this.state.albums.map(album =>
<AlbumDetail key={album.title} album={album} />);
}
render() {
console.log(this.state);
return (
<div>
<ScrollView></ScrollView>
{this.renderAlbums()}
</div>
);
}
}
export default AlbumList;
Related
import React from 'react';
import axios from 'axios';
import { Card } from 'antd';
class ArticleDetail extends React.Component {
state = {
article: {}
}
componentDidMount() {
const articleID = this.props.match.params.articleID;
axios.get(`http://127.0.0.1:8000/api/${articleID}`)
.then(res => {
this.setState({
article: res.data
});
})
}
render() {
return (
<div>
<Card title={this.state.article.title}>
<p>{this.state.article.content}</p>
</Card>
</div>
)
}
}
export default ArticleDetail;
//Make sure on your article.js file under title link you use ` instead of '
<List.Item.Meta
avatar={<Avatar src={item.avatar} />}
title={<a href={`/${item.id}`}>{item.title}</a>} //here
description={item.description}
/>
{item.content}
</List.Item>`
//And under your routes.js file make sure the (article Id) is the same as the one you used on the Article-detail-view
exact path='/:articleID' component={ArticleDetail}
What I'm Trying To Do
My current code is like this.
import React from 'react';
import {
Container, Header, Body, View, Content, Title, Text, Left, Right
} from 'native-base';
import 'react-native-gesture-handler';
import Fire from 'app/src/Fire';
import {
StyleSheet, Image, TouchableOpacity,
} from 'react-native';
export default class All extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
};
}
async componentDidMount() {
const querySnapshot = await Fire.shared.getItems(1);
const items = await Fire.shared.pushItems(querySnapshot);
this.setState({ items });
}
render() {
const { items } = this.state;
return (
<Container>
<View>
{items.map((item) => (
<Image
source={{ uri: item.first_img_url }}
/>
<View>
<Text>{item.name}</Text>
</View>
))}
</View>
</Container>
);
}
}
I have another component that has almost same code as above one.
The differences are class name and
await Fire.shared.getItems(1);
or
await Fire.shared.getItems(2);
I know I should combine the same code into one component.
I would appreciate it if you could give me any advices or tips :)
You can extract this code and pass the number 1 or 2 in props.
import React from 'react';
import {
Container, Header, Body, View, Content, Title, Text, Left, Right
} from 'native-base';
import 'react-native-gesture-handler';
import Fire from 'app/src/Fire';
import {
StyleSheet, Image, TouchableOpacity,
} from 'react-native';
export default class All extends React.Component {
constructor(props) {
super(props);
this.state = {
items: [],
};
}
async componentDidMount() {
const querySnapshot = await Fire.shared.getItems(this.props.nbrOfItems);
const items = await Fire.shared.pushItems(querySnapshot);
this.setState({ items });
}
render() {
const { items } = this.state;
return (
<Container>
<View>
{items.map((item) => (
<Image
source={{ uri: item.first_img_url }}
/>
<View>
<Text>{item.name}</Text>
</View>
))}
</View>
</Container>
);
}
}
You can call this component in any component like this
<All nbrOfItems={1} />
Or
<All nbrOfItems={2} />
I get this error that getInputData is undefined, please what am I doing wrong?
getInputData simply gets the users inputs....I'm using redux. I defined getInputData in my function called handleInput or is it not well defined......
import React from "react";
import styles from "./style";
import { Text } from "react-native";
import { View, Input, InputGroup } from "native-base";
import Icon from "react-native-vector-icons/FontAwesome";
import { SearchBar } from "react-native-elements";
export const SearchBox = ({ getInputData }) => {
const handleInput = (key, val) => {
getInputData({
key,
value: val
});
};
return (
<View style={styles.searchBox}>
<View style={styles.inputWrapper}>
<Text style={styles.label}>PICK UP</Text>
<InputGroup>
<Icon name="search" size={15} color="#FF5E3A" />
<Input
style={styles.inputSearch}
placeholder="Enter Pickup Location"
onChangeText={handleInput.bind(this, "pickUp")}
/>
</InputGroup>
</View>
<View style={styles.inputWrapper}>
<Text style={styles.label}>DROP OFF</Text>
<InputGroup>
<Icon name="search" size={15} color="#FF5E3A" />
<Input
style={styles.inputSearch}
placeholder="Enter Drop Off Location"
onChangeText={handleInput.bind(this, "dropOff")}
/>
</InputGroup>
</View>
</View>
);
};
export default SearchBox;
this is my mapContainer.js where inputData is passed down as a prop to SearchBox.
import React from 'react';
import styles from './style';
import {View} from 'native-base';
import MapView from 'react-native-maps';
import SearchBox from '../SearchBox';
import SearchResults from '../SearchResults';
export const MapContainer= ({region, getInputData}) => {
return(
<View style={styles.container}>
<MapView
provider={MapView.PROVIDER_GOOGLE}
style={styles.map}
region={region}
>
<MapView.Marker
coordinate={region}
pinColor="green"/>
</MapView>
<SearchBox getInputData={getInputData}/>
<SearchResults/>
</View>
)
}
export default MapContainer
This is where I connect mapStateToProps to my mapActionCreators
import {connect} from "react-redux";
import {
getCurrentLocation,
getInputData,
} from '../../actions/currentLocation';
import { MapContainer } from '../MapContainer';
import Home from "../../screens/Home";
const mapStateToProps=(state)=>({
region:state.region,
inputData:state.inputData || {}
});
const mapActionCreators = {
getCurrentLocation,
getInputData,
};
export default connect(mapStateToProps,mapActionCreators)(Home);
this is my Home code.
import React from 'react';
import { View, Text } from 'react-native';
import styles from './styles';
import { Container} from 'native-base';
import { MapContainer} from '../../components/MapContainer';
import GetLocation from 'react-native-get-location'
import {Dimensions} from "react-native";
const {width,height}=Dimensions.get("window");
const ASPECT_RATIO=width/height;
const LATITUDE_DELTA=0.922;
const LONGITUDE_DELTA=ASPECT_RATIO*LATITUDE_DELTA
class Home extends React.Component{
constructor(props){
super(props);
this.state={
latitude:3.14662,
longitude:101.6984,
latitudeDelta:LATITUDE_DELTA,
longitudeDelta:LONGITUDE_DELTA
}
}
componentDidMount(){
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then(location => {
this.setState({
latitude:location.latitude,
longitude:location.longitude
})
console.log(location)
console.log(this.state.longitude);
})
.catch(error => {
const { code, message } = error;
console.warn(code, message);
}) }
render(){
const region={
latitude:this.state.latitude,
longitude:this.state.longitude,
latitudeDelta:this.state.latitudeDelta,
longitudeDelta:this.state.longitudeDelta
}
return(
<Container>
<MapContainer region={region} getInputData={this.props.getInputData} />
</Container>
);
}
}
export default Home;
You should use connect on the Home page like
import React from 'react';
import {connect} from "react-redux";
import { View, Text } from 'react-native';
import styles from './styles';
import { Container} from 'native-base';
import { MapContainer} from '../../components/MapContainer';
import GetLocation from 'react-native-get-location'
import {Dimensions} from "react-native";
import {
getCurrentLocation,
getInputData,
} from '../../actions/currentLocation';
const {width,height}=Dimensions.get("window");
const ASPECT_RATIO=width/height;
const LATITUDE_DELTA=0.922;
const LONGITUDE_DELTA=ASPECT_RATIO*LATITUDE_DELTA
class Home extends React.Component{
constructor(props){
super(props);
this.state={
latitude:3.14662,
longitude:101.6984,
latitudeDelta:LATITUDE_DELTA,
longitudeDelta:LONGITUDE_DELTA
}
}
componentDidMount(){
GetLocation.getCurrentPosition({
enableHighAccuracy: true,
timeout: 15000,
})
.then(location => {
this.setState({
latitude:location.latitude,
longitude:location.longitude
})
console.log(location)
console.log(this.state.longitude);
})
.catch(error => {
const { code, message } = error;
console.warn(code, message);
}) }
render(){
const region={
latitude:this.state.latitude,
longitude:this.state.longitude,
latitudeDelta:this.state.latitudeDelta,
longitudeDelta:this.state.longitudeDelta
}
return(
<Container>
<MapContainer region={region} getInputData={this.props.getInputData} />
</Container>
);
}
}
const mapStateToProps=(state)=>({
region:state.region,
inputData:state.inputData || {}
});
const mapActionCreators = {
getCurrentLocation,
getInputData,
};
export default connect(mapStateToProps,mapActionCreators)(Home);
I'm trying to add simple React Context to my app. I create Context in "./components/DataProvider.js" that looks like this:
import React, { Component } from 'react'
const DataContext = React.createContext()
class DataProvider extends Component {
state = {
isAddButtonClicked: false
}
changeAddButtonState = () => {
if( this.state.isAddButtonClicked ) {
this.setState({
isAddButtonClicked: false
})
} else {
this.setState({
isAddButtonClicked: true
})
}
}
render() {
return(
<DataContext.Provider
value={{
isAddButtonClicked: this.state.isAddButtonClicked,
changeAddButtonState: () => {
if( this.state.isAddButtonClicked ) {
this.setState({
isAddButtonClicked: false
})
} else {
this.setState({
isAddButtonClicked: true
})
}
}
}}
>
{this.props.children}
</DataContext.Provider>
)
}
}
const DataConsumer = DataContext.Consumer
export default DataProvider
export { DataConsumer }
Which then I added to "./pages/_app.js"
import App, { Container } from 'next/app'
import DataProvider from '../components/DataProvider'
class MyApp extends App {
render () {
const { Component, pageProps } = this.props
return (
<Container>
<DataProvider>
<Component {...pageProps} />
</DataProvider>
</Container>
)
}
}
export default MyApp
And consume it in "./components/AddPostButton.js".
import React, {Component} from 'react'
import { DataConsumer } from './DataProvider'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faPlus } from '#fortawesome/free-solid-svg-icons'
class AddPostButton extends Component {
render() {
return (
<div>
<DataConsumer>
{({ changeAddButtonState }) => (
<a onClick={changeAddButtonState}>
<FontAwesomeIcon icon={faPlus} color='#fff' />
</a>
)}
</DataConsumer>
</div>
)
}
}
export default AddPostButton
But I get this error "Cannot read property 'changeAddButtonState' of undefined". I'm using React 16.7 and NextJS 7.0.2. Don't know what is wrong.
The second question is should I use one Context for everything or just use them as Model in MVC pattern?
I fixed it by moving changeAddButtonState to Context Component state so my DataProvider.js now looks like this
import React, { Component } from 'react'
const DataContext = React.createContext()
class DataProvider extends Component {
state = {
isAddButtonClicked: false,
changeAddButtonState: () => {
if (this.state.isAddButtonClicked) {
this.setState({
isAddButtonClicked: false
})
} else {
this.setState({
isAddButtonClicked: true
})
}
}
}
render() {
return(
<DataContext.Provider
value={this.state}
>
{this.props.children}
</DataContext.Provider>
)
}
}
const DataConsumer = DataContext.Consumer
export default DataProvider
export { DataConsumer }
And then in AddButton component I changed code to look like this
import React, {Component} from 'react'
import { DataConsumer } from './DataProvider'
import { FontAwesomeIcon } from '#fortawesome/react-fontawesome'
import { faPlus } from '#fortawesome/free-solid-svg-icons'
class AddPostButton extends Component {
constructor(props) {
super(props)
}
render() {
return (
<div>
<DataConsumer>
{(context) => (
<a onClick={context.changeAddButtonState}>
<FontAwesomeIcon icon={faPlus} color='#fff' />
</a>
)}
</DataConsumer>
</div>
)
}
}
export default AddPostButton
Maybe this doesen't even look like a code, but is there any way I can change other components value/state on click?
import React from 'react';
import './pokemonList.css';
import {Component} from 'react';
import Pokemon from './Pokemon';
class PokemonList extends Component {
constructor(props){
super(props);
this.state = {
pokemons : [],
pokemon : {}
};
}
componentWillMount(){
fetch('https://pokeapi.co/api/v2/pokemon/').then(res=>res.json())
.then(response=>{
this.setState({
pokemons : response.results,
});
});
}
handleClick(id) {
fetch(`https://pokeapi.co/api/v2/pokemon/${id}/`)
.then(res => res.json())
.then(data => {
const pokemon = new Pokemon(data);
this.setState({ pokemon: pokemon });
})
.catch(err => console.log(err));
console.log("click happened");
}
render(){
const {pokemons} = this.state;
return (
<div className='pokemonList'> {pokemons.map(pokemon =>(
<button onClick={this.handleClick.bind(this)} className='pokemon-
btn' key={pokemon.name}>
{pokemon.name}
</button>
))}
</div>
)
}}
export default PokemonList;
At this point I'm not even sure where does handleClick() has to be, so I put it in my App component aswell. The output is ok, but clicking these buttons doesen't seem to do anything. They are supposed to show detailed pokemon information in component.
import React, {Component} from 'react';
import './pokemon-info.css';
const PokemonInfo = ({ pokemon }) => {
const { name,
height,
weight,
sprite,
statsSpeed,
statsSpecialDefense,
statsSpecialAttack,
statsDefense,
statsAttack,
statsHp
} = pokemon;
return (
<section className="pokemonInfo">
<img src={sprite} className='sprite-image' alt="pokemon_sprite"/>
<div className='data-wrapper'>
<h3 className="data-char">{pokemon.name}</h3><br />
<p className = 'data-char'>Height: {height}</p>
<p className = 'data-char'>Weight: {weight}</p><br />
<p className = 'data-char'>Stats: </p><br />
<p className = 'data-char'>Speed: {statsSpeed}</p>
<p className = 'data-char'>Special defense: {statsSpecialDefense}</p>
<p className = 'data-char'>Special attack: {statsSpecialAttack}</p>
<p className = 'data-char'>Defense: {statsDefense}</p>
<p className = 'data-char'>Attack: {statsAttack}</p>
<p className = 'data-char'>Hp: {statsHp}</p>
</div>
</section>
)
}
export default PokemonInfo;
Here is my App component
import React, { Component } from 'react';
import './App.css';
import PokemonList from './PokemonList';
import Pokemon from './Pokemon';
import PokemonInfo from './PokemonInfo';
class App extends Component {
constructor() {
super();
this.state = {
pokemon: {}
};
this.handleOnClick = this.handleOnClick.bind(this);
}
handleOnClick(id) {
fetch(`http://pokeapi.co/api/v2/pokemon/${id}/`)
.then(res => res.json())
.then(data => {
const pokemon = new Pokemon(data);
this.setState({ pokemon });
})
.catch(err => console.log(err));
}
render() {
return (
<div className="App">
<PokemonList />
<PokemonInfo pokemon={this.state.pokemon}/>
</div>
);
}
}
export default App;
It is obvious I did go wrong somewhere, but where?
Update:
Pokemon
class Pokemon {
constructor(data) {
this.id = data.id;
this.name = data.name;
this.height = data.height;
this.weight = data.weight;
this.sprite = data.sprites.front_default;
this.statsSpeed = data.stats[0].stats.base_stat;
this.statsSpecialDefense = data.stats[1].stats.base_stat;
this.statsSpecialAttack = data.stats[2].stats.base_stat;
this.statsDefense = data.stats[3].stats.base_stat;
this.statsAttack = data.stats[4].stats.base_stat;
this.statsHp = data.stats[5].stats.base_stat;
}
}
export default Pokemon;
Your App component should keep the state and pass updater functions as props to children components:
PokemonList
import React from "react";
import "./pokemonList.css";
import { Component } from "react";
import Pokemon from "./Pokemon";
class PokemonList extends Component {
render() {
const { pokemons } = this.props;
return (
<div className="pokemonList">
{pokemons.map(pokemon => (
<button
onClick={() => this.props.handleClick(pokemon.id)} // id or whatever prop that is required for request
className="pokemon-btn"
key={pokemon.name}
>
{pokemon.name}
</button>
))}
</div>
);
}
}
PokemonInfo - no change here.
APP
import React, { Component } from "react";
import "./App.css";
import PokemonList from "./PokemonList";
import Pokemon from "./Pokemon";
import PokemonInfo from "./PokemonInfo";
class App extends Component {
constructor() {
super();
this.state = {
pokemon: {},
pokemons: [],
};
this.handleOnClick = this.handleOnClick.bind(this);
}
componentDidMount() {
fetch("https://pokeapi.co/api/v2/pokemon/")
.then(res => res.json())
.then(response => {
this.setState({
pokemons: response.results
});
});
}
handleOnClick(id) {
fetch(`http://pokeapi.co/api/v2/pokemon/${id}/`)
.then(res => res.json())
.then(data => {
const pokemon = new Pokemon(data);
this.setState({ pokemon });
})
.catch(err => console.log(err));
}
render() {
return (
<div className="App">
<PokemonList pokemons={this.state.pokemons} handleClick={this.handleOnClick} />
<PokemonInfo pokemon={this.state.pokemon} />
</div>
);
}
}
More on lifting the state up.