I'm trying to have access to an element from an array of objects in the React state. This array is the result of a fetch command. I know that I need to write a "map" ,but the map will result in getting the whole elements of the array.
Here's the code I have written :
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
// function App() {
// } instead we'll use class component
class App extends Component{
constructor(){
super();
this.state = {
guys:[]
}
};
componentDidMount(){
fetch('https://jsonplaceholder.typicode.com/users')
.then(response=>response.json())
.then(user=>this.setState({guys:user}))
}
render(){
//we'll add the return part from the stateless component
return (
<div className="App">
{this.state.guys.map(guy=>(
<h2 > {guy.email} </h2>
))}
</div>
);
}
}
export default App;
The result of the code above is :
Sincere#april.biz
Shanna#melissa.tv
Nathan#yesenia.net
Julianne.OConner#kory.org
Lucio_Hettinger#annie.ca
Karley_Dach#jasper.info
Telly.Hoeger#billy.biz
Sherwood#rosamond.me
Chaim_McDermott#dana.io
Rey.Padberg#karina.biz
However,I would like to access to only one element,for example, the ideal outcome must be Shanna#melissa.tv.
I have tried <h1>this.state.guys[1].email</h1>, but this approach seems to be ineffective and I know I should do sth else. Do you have any idea on how to access and manipulate only one (or some) of the states?
Related
i'm working on react router project (im beginner) to improve my skillz.
my problem:
I have a JSON file (Dragon ball Z). When i click on characters (like goku) i want to show his biography.
Actually when i click on goku every biography of each characters are showed.
I know how to do it with function component (useLocation ect..) but i'm totally stuck wicth class component because i can't use hooks in it, what is the good way to do it ?
here is the project :
DBZ REACT ROUTES
Thanks
Using react-router-dom v6 we can use useParams to read the params key within function component.
With class component you can write an HOC function to archive the same thing
a higher-order component is a function that takes a component and returns a new component
import { useParams } from 'react-router-dom'
function withParams(Component) {
return props => <Component {...props} params={useParams()} />;
}
class Charbio extends React.Component {
componentDidMount() {
let { id } = this.props.params;
// get the bio by id here
}
render() {
return API.map((element) => {
return <p>{element.bio}</p>;
});
}
}
export default withParams(Charbio);
so I am new to React. Loving it so far. However, I am having a basic question which doesn't have a clear answer right now.
So, I am learning how to lift the state of a component.
So here's a reproducible example.
index.js
import React from "react";
import ReactDOM from "react-dom"
import {Component} from "react";
// import AppFooter from "./AppFooter";
import AppContent from "./AppContent";
import AppHeader from "./AppHeader";
import 'bootstrap/dist/css/bootstrap.min.css'
import 'bootstrap/dist/js/bootstrap.bundle.min'
import './index.css'
class App extends Component{
constructor(props) {
super(props);
this.handlePostChange = this.handlePostChange.bind(this)
this.state = {
"posts": []
}
}
handlePostChange = (posts) => {
this.setState({
posts: posts
})
}
render() {
const headerProps = {
title: "Hi Keshav. This is REACT.",
subject: "My Subject is Krishna.",
favouriteColor: "blue"
}
return (
<div className="app">
<div>
<AppHeader {...headerProps} posts={this.state.posts} handlePostChange={this.handlePostChange}/>
<AppContent handlePostChange={this.handlePostChange}/>
</div>
</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById("root"))
I am trying to lift the state of posts which is changed in AppContent to AppHeader.
Here's my AppContent.js and AppHeader.js
// AppContent.js
import React, {Component} from "react";
export default class AppContent extends Component{
state = {
posts: []
}
constructor(props) {
super(props); // constructor
this.handlePostChange = this.handlePostChange.bind(this)
}
handlePostChange = (posts) => {
this.props.handlePostChange(posts)
}
fetchList = () => {
fetch("https://jsonplaceholder.typicode.com/posts")
.then((response) =>
response.json()
)
.then(json => {
// let posts = document.getElementById("post-list")
this.setState({
posts: json
})
this.handlePostChange(json)
})
}
clickedAnchor = (id) => {
console.log(`Clicked ${id}`)
}
render() {
return (
<div>
<p>This is the app content.</p>
<button onClick={this.fetchList} className="btn btn-outline-primary">Click</button>
<br/>
<br/>
<hr/>
<ul>
{this.state.posts.map((item) => {
return (
<li id={item.id}>
<a href="#!" onClick={() => this.clickedAnchor(item.id)}>{item.title}</a>
</li>
)
})}
</ul>
<hr/>
<p>There are {this.state.posts.length} entries in the posts.</p>
</div>
)
}
}
// AppHeader.js
import React, {Component, Fragment} from "react";
export default class AppHeader extends Component{
constructor(props) {
super(props); // constructor
this.handlePostChange=this.handlePostChange.bind(this)
}
handlePostChange = (posts) => {
this.props.handlePostChange(posts)
}
render() {
return (
<Fragment>
<div>
<p>There are {this.props.posts.length} posts.</p>
<h1>{this.props.title}</h1>
</div>
</Fragment>
)
}
}
So here's the main question. As we see, that I am calling the dummy posts api and trying to show the titles of the json object list returned by it.
The posts state is actually updated in AppContent and is shared to AppHeader by lifting it to the common ancestor index.js
However, here's what I have observed.
When I keep this code running using npm start I see that anytime I make a change in any place, it refreshes. I was under the impression that it renders the whole page running on localhost:3000.
Say here's my current situation on the web page:
Now, say I make a change in just AppContent.js, then here's how it looks then:
In here, we see that it's still showing 100 posts in case of AppHeader. Is this expected that react only reloads the component and not the whole page. When I refresh the whole page, it shows 0 posts and 0 posts in both the places. Now have I made a mistake in writing the code ? If yes, how do I fix this ?
Thank you.
In case the question is not clear please let me know.
In here, we see that it's still showing 100 posts in case of AppHeader. Is this expected that react only reloads the component and not the whole page.
It's not React, per se, that's doing that. It's whatever you're using to do hot module reloading (probably a bundler of some kind, like Webpack or Vite or Rollup or Parcel or...). This is a very handy feature, but yes, it can cause this kind of confusion.
Now have I made a mistake in writing the code ?
One moderately-signficant one, a relatively minor but important one, and a couple of trivial ones:
posts should either be state in App or AppContent but not both of them. If it's state in both of them, they can get out of sync — as indeed you've seen with the hot module reloading thing. If you want posts to be held in App, fetch it there and provide it to AppContent as a property. (Alternatively you could remove it from App and just have it in AppContent, but then you couldn't show the total number of posts in App.)
When you're rendering the array of posts, you need to have a key on each of the li items so that React can manage the DOM nodes efficiently and correctly.
There's no need to wrap a Fragment around a single element as you are in AppHeader.
If you make handlePostChange an arrow function assigned to a property, there's no reason to bind it in the constructor. (I would make it a method instead, and keep the bind call, but others like to use an arrow function and not bind.)
There's no reason for the wrapper handlePostChange functions that just turn around and call this.props.handlePostChange; just use the function you're given.
Two issues with your fetch call:
You're not checking for HTTP success before calling json. This is a footgun in the fetch API I describe here on my very old anemic blog. Check response.ok before calling response.json.
You're ignoring errors, but should report them (via a .catch handler).
I'm trying to implement methods to the React import of PESDK (PhotoEditorSDK).
I have an App.js that imports Header, BodyLeft and BodyMiddle without relation between them.
BodyMiddle.js is a template component that renders :
// src/components/BodyMiddle/index.js
import React, { Component } from "react";
import "./BodyMiddle.css";
class BodyMiddle extends Component {
constructor(props){
super(props);
}
handleClick(e) {
e.preventDefault();
// Nothing yet
}
render() {
return (
<div id="BodyMiddle">
<div><button id="resetEditor" onClick={(e) => this.handleClick(e)}>Reset Editor</button></div>
<div class="photo-editor-view"></div>
</div>
);
}
}
export default BodyMiddle;
PhotoEditor.js is the component that calls the PESDK :
// src/components/PhotoEditor/index.js
import React from 'react'
import ReactDOM from 'react-dom'
window.React = React
window.ReactDOM = ReactDOM
import "./PhotoEditor.css";
import "photoeditorsdk/css/PhotoEditorSDK.UI.ReactUI.min.css";
import PhotoEditorUI from 'photoeditorsdk/react-ui'
class PhotoEditor extends React.Component {
constructor(props){
super(props);
}
resetEditor(){
// Empty the image
return this.editor.ui.setImage(new Image());
}
render() {
const { ReactComponent } = PhotoEditorUI;
return (
<div>
<ReactComponent
ref={c => this.editor = c}
license='licence_removed_for_snippet'
assets={{
baseUrl: './node_modules/photoeditorsdk/assets'
}}
editor={{image: this.props.image }}
style={{
width: "100%",
height: 576
}} />
</div>)
}
}
export default PhotoEditor;
Note that the photo-editor-view div class is rendered in BodyLeft.js, by calling the following code and it works well:
ReactDOM.render(<PhotoEditor ref={this.child} image={image} />, container);
Where container is (and I pass an image somewhere else) :
const container = document.querySelector('.photo-editor-view');
What I'm trying to achieve
I would like to keep the reset Button inside BodyMiddle, which is independant and called from App.js, in order to call the PhotoEditor component on the method resetEditor() from anywhere in my app.
That way I could have separated template files that interract with each other.
I've done research and I did not really find an answer yet, I know that React might not be the lib for that, but what are the options ? I see more and more React live apps running with a lot of components interacting, I'm curious.
Thank you for your time !
Best regards
You can use ref on PhotoEditor and save that ref in App, and in the App you can have a method called onResetEditor which calls the ref.resetEditor.
Now you can pass onResetEditor to BodyMiddle or any other component.
Read more about refs in React https://reactjs.org/docs/refs-and-the-dom.html
Say I have a comp that is inside of a Scene (react-native-router-flux). It lets people choose their favorite fruits.
import React, {Component} from 'react';
import {View, Text, StyleSheet} from 'react-native';
import {MKCheckbox} from 'react-native-material-kit';
var styles = StyleSheet.create({});
export default class PickAFruit extends Component {
render() {
console.log(this.props.fruits);
return (
<View>
{
this.props.fruits.map((x)=> {
return (
<View key={x.key}>
<Text>{x.key}</Text>
<MKCheckbox checked={this.props.checked} key={x.key} onCheckedChange={(e) => {
this.props.update(e, '' + x.key)
}}/>
</View>
)
})
}
</View>
)
}
}
In the parent comp I'm loading the list of fruits from an API in the didMount:
componentDidMount() {
ApiInst.getFruits().then((fruits) => {
console.log(fruits);
console.log(this.props.fruits);
this.props.fruits = fruits;
});
}
I'm also setting a default fruits array in the parent class. It seems like the properties won't load via the API though, the list of fruit is always the "unknown" value, never the new values. Do I need to load the list of fruits before the Profile scene is loaded? When is the correct time to set properties for a component if they will come from an API?
setState seems like the easy answer but these settings don't "feel" like state, they feel like properties that would be injected at build-time (i.e. when the component is built, not the app). Is this a distinction without a real difference?
You can't modify props. Props are passed from parent to child component, and only the parent can change them.
Use setState instead:
this.setState({fruits: fruits});
And access them from state:
<PickAFruit fruits={this.state.fruits} />
You may also want to set a default state in the component constructor:
constructor(props) {
super(this);
this.state = {fruits: null};
}
this.props.fruits = fruits;
won't effect child component, and to be honest - I'm not sure it will work at all. If you don't want to use flux architecture I think the best solution is to update parent's state on componentDidMount() and pass it as props to child component:
componentDidMount() {
ApiInst.getFruits().then((fruits) => {
this.setState({fruits: fruits});
});
}
render() {
return (
<PickAFruit fruits={this.state.fruits} />
);
}
Every state change will invokre render() method, so after API call PickAFruit component will be rerendered, with fruits passed as a props.
Suppose I have a small reusable component called <LikePanel> which will be used across multiple pages in different types of parent components, like <BlogEntry> or <ItemEntry> or <ReplyEntry>.
import {connect} from 'react-redux'
import {likeAction} from './LikeAction'
class LikePanel extends React.Component{
render() {
return <ButtonGroup className={this.props.className}>
<Button onClick={()=>this.onClickLiking()}>
<Glyphicon glyph="thumbs-up"/>{this.props.like}</Button>
</ButtonGroup>
}
onClickLiking(type){
this.props.dispatch(likeAction());
}
}
const mapStateToProps = state => {
let obj = {};
obj[LIKE] = state[LIKE];
return obj;
}
export default connect(mapStateToProps)(LikePanel)
Example use cases of LikePanel:
class BlogEntry extends React.Component{
render(){
return this.props.data.entry.map((item)=>{
return <div>
{item.article}
<LikePanel like={item.like}/>
</div>
}
}
}
class ProductEntry extends React.Component{
render(){
return this.props.data.entry.map((item)=>{
return <div>
<ProductPanel data={item}/>
<LikePanel like={item.like}/>
</div>
}
}
}
So if a webpage has 20 blog entries there will be 20 connected <LikePanel> on the page, and there is a possibility in the future that extra components will be connected to redux. Is it a good practice to use connect() with such a small components like <LikePanel>?
It's absolutely fine. Use connect wherever it makes sense in your component hierarchy. One common pattern is to have a list component be connected and use mapState to retrieve the IDs of the data items in the list, render some <ListItem id={itemId} /> child component for each item, and have each child component also be connected and look up its own data by ID. Also see the Redux FAQ question at https://redux.js.org/faq/react-redux#should-i-only-connect-my-top-component-or-can-i-connect-multiple-components-in-my-tree .