Fetching api and sending the state using Context api - reactjs

This is my App.js below and on fetching the data and setState I am
unable to display the date in child, however my data is fetched in
console log. Even i have MyContext in separate file
//App.js
class MyProvider extends Component {
state = { userData: '' }
componentWillMount() {
fetch('https://randomuser.me/api/')
.then(res => res.json())
.then(userData => {
this.setState({ userData: userData })
console.log('here is list =')
console.log(userData);
});
}
render() {
return (
<MyContext.Provider value={{ state: this.state }}>
{this.props.children}
</MyContext.Provider>
);
}
}
class App extends Component {
render() {
return (
<div>
<MyProvider value={{ state : this.state }}>
<Header />
<User />
</MyProvider>
</div>
);
}
}
export default App;
//User.js
//Cannot display gender , email in below code
class User extends Component {
render() {
return (
<div>
<MyContext.Consumer>
{
(context) => (
<React.Fragment>
<p>Gender : {context.state.gender}</p>
<p>Email : {context.state.email}</p>
</React.Fragment>
)
}
</MyContext.Consumer>
</div>
);
}
}
export default User;

In the Context Provider you are setting the data inside userData state and hence you need to access it in consumer like context.state.userData.gender. Also since userData is an object after the response is available, you should initialise it as an object in Provider constructor as well otherwise you would need to add a check in the User component because accessing email and gender from userData
class MyProvider extends Component {
state = { userData: {} }
componentWillMount() {
fetch('https://randomuser.me/api/')
.then(res => res.json())
.then(userData => {
this.setState({ userData: userData })
console.log('here is list =')
console.log(userData);
});
}
render() {
return (
<MyContext.Provider value={{ state: this.state }}>
{this.props.children}
</MyContext.Provider>
);
}
}
class App extends Component {
render() {
return (
<div>
<MyProvider value={{ state : this.state }}>
<Header />
<User />
</MyProvider>
</div>
);
}
}
export default App;
User.js
class User extends Component {
render() {
return (
<div>
<MyContext.Consumer>
{
(context) => (
<React.Fragment>
<p>Gender : {context.state.userData.gender}</p>
<p>Email : {context.state.userData.email}</p>
</React.Fragment>
)
}
</MyContext.Consumer>
</div>
);
}
}
export default User;

Related

Data is not flowing down to prop

I am having an issue getting data to flow down to my props to where when component rendered, the props are not displaying.
This is the container that contains my RecipeList Component
*---Note: I am getting my data asynchronously from a api btw *
import { postRecipes } from '../actions/postRecipes.js'
import { getRecipes } from '../actions/getRecipes'
class RecipesContainer extends Component{
constructor(props){
super(props)
}
componentDidMount(){
this.props.getRecipes()
}
render(){
return (
<div>
<RecipeInput postRecipes={this.props.postRecipes} />
<RecipeList recipes={this.props.recipes} />
</div>
)
}
}
const mapStateToProps = state =>{
return{
recipes: state.recipes
}
}
const mapDispatchToProps = dispatch =>{
return{
postRecipes: (recipe) => dispatch(postRecipes(recipe)),
getRecipes: () => dispatch(getRecipes())
// deleteRecipe: id => dispatch({type: 'Delete_Recipe', id})
}
}
export default connect(mapStateToProps,mapDispatchToProps)(RecipesContainer)
Here is my RecipeList component
import React, {Component} from 'react';
import Recipe from './Recipe.js'
class RecipeList extends Component {
render() {
const { recipes } = this.props
return (
<div>
{recipes.map((recipe,index) => <Recipe recipe={recipe} key={index} />)}
</div>
)
}
}
export default RecipeList;
And here is the Recipe component that it mapping as I enter and submit a recipe
import React, {Component} from 'react'
class Recipe extends Component {
render(){
return(
<div>
<h3>Name: {this.props.name}</h3>
<p>Category:{this.props.category}</p> <-------this one I will have to call differently since this is a one to many relationship
<p>Chef Name: {this.props.chef_name}</p>
<p>Origin: {this.props.origin}</p>
<p>Ingredients: {this.props.ingredients}</p>
</div>
)
}
}
export default Recipe
EDIT: Added getRecipe action as requested.
export const getRecipes = () => {
const BASE_URL = `http://localhost:10524`
const RECIPES_URL =`${BASE_URL}/recipes`
return (dispatch) => {
dispatch({ type: 'START_FETCHING_RECIPES_REQUEST' });
fetch(RECIPES_URL)
.then(response =>{ return response.json()})
.then(recipes => { return console.log(recipes), dispatch({ type: 'Get_Recipes', recipes })});
};
}
Why isn't it displaying my results? I did console to make I was return my api data, and the Recipe component is rendering as just the html tags are rendering just fine.
You pass in a prop called recipe to your <Recipe /> component, but your component reads from a non-existant this.props.name, etc.
In your recipe list component, try this.
{recipes ? recipes.map((recipe,index) => <Recipe recipe={recipe} key={index} />) : null}

React Re-Render Component on props Change

I have a Tabbar in my Tabbar Component, Which I Change the index props in it :
class Tabbar extends Component {
state = {
index: this.props.index,
name: this.props.name,
image: this.props.image
};
changeTabs = () => {
this.setState({index: this.props.index});
}
render() {
return (
<React.Fragment>
<div id={this.state.index} className="col">
<button onClick={this.changeTabs}></button>
</div>
</React.Fragment>
);
}
}
export default Tabbar;
And Then In my Other Component, I Wanna Re-Render a fragment after props change. Here's my Code :
import Tabbar from './Tabbar';
class Tabview extends Component {
constructor(props) {
super(props);
this.state = {
tabs: [
{index: 0, name: "tab0", image:require('../Assets/profile.svg'),childView: {ProfilePage} },
{index: 1, name: "tab1", image:require('../Assets/home.svg'),childView: {HomePage}},
{index: 2, name: "tab2", image:require('../Assets/blog.svg'),childView: {BlogPage}},
],
}
}
handleRender = () => {
this.state.tabs.map(item => {
if (item.index === this.props.index) {
return <item.childView/>;
}
})
return <BlogPage/>;
}
render() {
return (
<div>
<Header/>
{this.handleRender()}
{this.state.tabs.map(item =>
<Tabbar key={item.index} index={item.index} name={item.name} image={item.image}/>
)}
</div>
);
}
}
export default Tabview;
The Method "handleRender" should handle the rendering.
I tried to use "componentDidMount" or "componentDidUpdate", But I didn't work.
How Can I Make it Work?
Thank you in advance!
You dont need to have a state in the child component for this reason
You can simply have a callback in parent and call it in child component like below.
import React, { Component } from "react";
class Tabbar extends Component {
render() {
return (
<React.Fragment>
<div id={this.props.index} className="col">
<button
onClick={() => this.props.changeTabs(this.props.index)}
></button>
</div>
</React.Fragment>
);
}
}
export default Tabbar;
And in parent you maintain the active index state
import Tabbar from "./Tabbar";
import React, { Component } from "react";
class Tabview extends Component {
constructor(props) {
super(props);
this.state = {
tabs: [
//your tabs
],
activeIndex: 0
};
}
handleRender = () => {
this.state.tabs.map((item) => {
if (item.index === this.state.activeIndex) {
return <item.childView />;
}
});
return <div />;
};
render() {
return (
<div>
{this.handleRender()}
{this.state.tabs.map((item) => (
<Tabbar
key={item.index}
index={item.index}
name={item.name}
image={item.image}
changeTabs={(index) => this.setState({ activeIndex: index })}
/>
))}
</div>
);
}
}
export default Tabview;

How to trigger a function from another component in react native?

import { copilot, walkthroughable, CopilotStep } from 'react-native-copilot';
class DashboardContent extends Component {
state ={
secondStepActive: true
};
componentDidMount() {
this.props.start()
this.props.copilotEvents.on('stepChange', this.handleStepChange);
}
handleStepChange = step => {
console.log(`Current step is: ${step.name}`);
};
render() {
return (
<View> ...... <View/>
);
}
}
export default copilot({
animated: true,
overlay: 'svg',
})(DashboardContent);
I am using the react-native-copilot library for a walkthrough. I wish to trigger the this.props.start() function which starts the walkthrough using a button from my NavBar component - The _infoPage function in the code below should trigger the function basicaly.
The code for the Navbar is :
class NavBar extends Component {
state ={
isModalVisible: false,
email:'',
emailError: false,
emailErrorMessage: '',
};
_profileEdit() {
Actions.profileedit();
}
_notificationsPage(){
Actions.notifications();
}
_infoPage = () =>{
this.props.toggleTour();
}
toggleModal = () => {
this.setState({isModalVisible: !this.state.isModalVisible});
};
render() {
const {index, routes} = this.props.tabs;
console.log(index);
return (
<SafeAreaView>
<View style={styles.container}>
<StatusBar />
<TouchableOpacity onPress={this._infoPage}>
<MaterialCommunityIcons name="information-outline" size={24} color="#979797" style={{padding:10}}/>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}
}
function mapStateToProps(state){
return {
tabs : state.tabs
}
}
function mapDispatchToProps(dispatch){
return {
changeCounter : (index) => dispatch({type:'PAGE_CHANGED',payload: index}),
toggleTour: () => dispatch({
type: 'TOUR_OPENED'
})
}
}
export default connect(mapStateToProps, mapDispatchToProps)(NavBar);
I was thinking of putting this.props.start() inside a function and calling the function from another component.
How do I go about this?
Here is my App.js
export default class App extends Component {
render() {
return (
<Provider store = {store}>
<Routes />
</Provider>
);
}
}
The call for the NavBar is in Routes:
export default class Routes extends Component {
render() {
return (
<Router navBar={TopNavbar}>
<Scene key="root">
...
</Scene>
</Router>
);
}
}
You could pass the function as a prop in the other component, for example in your render function you could do the following:
render() {
return (
<View>
<YourComponent startTutorial={this.props.start}></YourComponent>
</View>
);
}
Then in YourComponent call this.props.startTutorial(); from a button or any event.
** Update
So in your case you can do the following:
export default class App extends Component {
render() {
return (
<Provider store = {store}>
<Routes /*here*/ startTutorial={this.props.start} />
</Provider>
);
}
}
export default class Routes extends Component {
/*here*/
CTopNavbar = ({ children }) => (
<TopNavbar startTutorial={this.props.startTutorial}>
{children}
</TopNavbar>
);
render() {
return (
<Router navBar={/*here*/CTopNavbar}>
<Scene key="root">
...
</Scene>
</Router>
);
}
}
Then in NavBar run this.props.startTutorial(); in your button.

ReactJS error TypeError: Cannot read property 'map' of undefined

I'm attempting to consume a JSON API using fetch; the error mentioned above appears on the following line: **this.state.data.map( (dynamicData,key)=>**
This is my ReactJS code with the error line in bold:
import React, { Component } from 'react';
import './App.css';
class App extends Component {
//constructor
constructor() {
super();
this.state = {
data: [],
}
} //end constructor
componentDidMount(){
return fetch('https://jsonplaceholder.typicode.com/todos')
.then((response)=>response.json())
.then((responseJson)=>
{
this.setState({
data:responseJson.todos
})
console.log(this.state.data)
})
} // end component did mount
render() {
return (
<div>
<h2>Todo:</h2>
<div>
{
**this.state.data.map( (dynamicData,key)=>**
<div>
<span> {dynamicData.userId} </span>
<span> {dynamicData.id} </span>
</div>
)
}
</div>
</div>
);
}
}
export default App;
Could I get some help as to what I'm doing wrong? Thanks in advance
import React, { Component } from "react";
import { render } from "react-dom";
class App extends Component {
state = {
data:[],
url: "https://jsonplaceholder.typicode.com/todos"
};
componentDidMount() {
fetch(this.state.url)
.then(response => response.json())
.then(data => this.setState({ data }));
}
render() {
const { data } = this.state;
data && console.log(data);
return (
<div>
{data &&
data.map(item => <div> Hello User With Id: {item.userId} </div>)}
</div>
);
}
}
render(<App />, document.getElementById("root"));
Your didMount should look like mine also, setState takes a callback so if you wanted to see what the data looked like it would be like this
this.setState({ data }, () => console.log(this.state.data))
In your render it looks like you forgot the parenthesis after the arrow function in map.
render() {
return (
<div>
<h2>Todo:</h2>
<div>
{
this.state.data.map((dynamicData,key)=> (
<div>
<span> {dynamicData.userId} </span>
<span> {dynamicData.id} </span>
</div>
))
}
</div>
</div>
);
}

React - fetched data isn't displayed

could anyone tell me why is that won't work? Proper data is displaying in the console (console.log(this.state);), but it won't be transfered to MainContainer.
Same data initialized in the constructor>state>users working without issues. Where's the problem?
App
import React, {Component} from 'react';
import logo from './logo.svg';
import './App.css';
import Header from './components/header/Header';
import MainContainer from './containers/main-container/MainContainer';
class App extends Component {
constructor(props) {
super(props);
this.state = {
users: []
}
}
componentDidMount() {
fetch('https://jsonplaceholder.typicode.com/users')
.then(response => response.json())
.then(users => {
let u = users.map((user) => {
return {id: user.id, name: user.name, email: user.email}
})
return u;
})
.then(u => {
this.setState({users: u});
console.log(this.state);
});
}
render() {
return (
<div className="App">
<Header/>
<MainContainer users={this.state.users}/>
</div>
)
}
}
export default App;
MainContainer
import React from 'react';
import ActionBar from '../../components/action-bar/ActionBar'
import ListHeader from '../../components/list-header/ListHeader'
import ListItem from '../../components/list-item/ListItem'
import ListItemPlaceholder from '../../components/list-item-placeholder/ListItemPlaceholder'
class MainContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
users : props.users
}
}
render() {
const list = this.state.users.map(
(user) =>
{
const liStyle = {
'background-color': user % 2 == 0 ? '#fbfcfc' : 'transparent',
};
return <ListItem key={user.id} style={liStyle} id={user.id} name={user.name} email={user.email}/>
}
);
return (
<div className={'main-container'}>
<ActionBar />
<ListHeader />
{list}
</div>
)
}
}
export default MainContainer;
.................................................................................................................
Best Regards!
crova
In your <MainContainer> component you store the users in its state in the constructor but you never alter it. You only need to use state when the component needs to alter it during its lifetime. But the users come from it's parent via the users prop which you never render. So just render that prop instead:
const MainContainer = props => (
<div className="main-container">
<ActionBar />
<ListHeader />
{props.users.map(({id, name, email}) => (
<ListItem
key={id}
style={{
backgroundColor: id % 2 === 0 ? '#fbfcfc' : 'transparent'
}}
id={id}
name={name}
email={email}
/>
))}
</div>
);
When the users change in the parent it will re-render and pass the new users array to the <MainContainer>.
Also note that if your component only renders props and has no own state it can be written as a stateless functional component.

Resources