app.js:
import './App.css';
import HttpService from '../services/http-service'
import React, { Component } from 'react';
const http = new HttpService()
class App extends Component {
constructor(props){
super(props)
this.state = {accounts: ""}
this.loadData = this.loadData.bind(this)
}
loadData = () => {
http.getAccounts().then(res =>{
this.setState({accounts: res})
console.log(this.accounts)
return res
}, err => {console.log(err)})
}
render() {return (
<div className="container">
<h1 className="title">Retail API</h1>
<DisplayAcc accounts = {this.accounts} />
</div>
)}
}
export default App;
on DisplayAcc, I have a console.log(this.props.accounts) in the constructor.
Output is undefined. What should I do?
I have tried adding these line:
componentDidMount(){
this.loadData = this.loadData.bind(this)
}
Still undefined. Please point out the error or if you have any suggestions/best practices I would highly appreciate that because I'm very new to this. Thanks!
In order to access the accounts state to be passed down from the App component, to your DisplayAcc commponent:
render() {return (
<div className="container">
<h1 className="title">Retail API</h1>
<DisplayAcc accounts = {this.state.accounts} />
</div>
)}
You should be able to access the state by you are trying to reach it by this.accounts
change it too:
this.state.accounts
It's more popular to use so called functional components nowadays in the community. By this approach you wouldnot need the constructor, really recommend reading about this since it will simplify your code quite a bit!
I don't see where loadData is called, which makes me suspicious about the value of accounts.
Consistency is key: if accounts is part of the component's state, then you should access it via this.state.accounts, rather than this.accounts.
Please notice that you console.log meaningfully. Hence, if you want to check this.account, there's no point printing res, as you mentioned in your comment.
First of all, this.loadData = this.loadData.bind(this) is unnecessary because loadData is an arrow function.
Secondly, setState is an async function so you can't read new state just after calling it. So, the following console log will be undefined
this.setState({accounts: res})
console.log(this.accounts) // also this must be this.state.accounts!
Moreover, you are trying to get state value in a wrong way, it must be:
<DisplayAcc accounts={this.state.accounts} />
If you want to read accounts prop in DisplayAcc component, you should add your console log to whether in render or componentDidUpate methods because constructor is only called on first render and your accounts props is empty at that time. But the ones I mentioned are called every time the props are changed.
Related
In React docs in HOC section, there is an example
const CommentListWithSubscription = withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
);
Could you please explain how a function scope works?
They put the second param as an arrow function, in this arrow function we have DataSource param and return a result of DataSource.getComments()
realization of HOC withSubscription
function withSubscription(WrappedComponent, selectData) {
return class extends React.Component {
constructor(props) {
super(props);
this.handleChange = this.handleChange.bind(this);
this.state = {
data: selectData(DataSource, props)
};
}
...
Here they use selectData as that function (DataSource) => DataSource.getComments()
and fire that function again with DataSource param
This moment a bit confused, is it the same DataSource that we put in arrow function above or different? and in general how does it work?
DataSource inside the HOC (withSubscription) is an existing variable in that scope or global scope, likely obtained via static import or a context.
DataSource in the parent (the code that calls the HOC) is just a place-holding parameter. Basically the parent is telling the HOC: "I don't know what data source you're using, just retrieve the comments from it (DataSource.getComments()) and use it as your state data".
If the parent wants another HOC instance to use different data (like blog post in the example), it just changes the instruction to DataSource.getBlogPost() for that HOC, possibly using some extra parameters passed via the HOC's props, like in the example. This pattern makes HOCs as flexible as it should be.
HOC concept
Any hoc is a normal function which returns a react Component , but with modified props
considering example in react docs :
const CommentListWithSubscription = withSubscription(
CommentList,
(DataSource) => DataSource.getComments()
);
I agree this is definitely not a easiest example to prefer.
consider a simple HOC withData
const withData = (component, endpoint) => {
function WrappedComponent(props) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(endpoint).then((res) => setData(res));
}, []);
return (
<component data={data} {...props} />
);
}
return WrappedComponent;
};
const ComponentWithData = withData(MyCustomComponent, api_endpoint)
function MyCustomComponent (props){
const { data } = props
// use this data to do whatever
}
when a prop is injected into <component/> like data={[1,2,3]}, that prop can be accesible through props of resulting component like props.data
withSubscription takes 2 arguments: WrappedComponent and selectData.
Notice that in the constructor of withSubscription they set the initial state to equal the result of invoking selectData. selectData is called with 2 arguments: DataSource is the first argument and props is the second.
In this case DataSource is probably some module they imported previously but just didn't show you...
When they wrap a CommentListWithSubscription with "withSubscription", the DataSource there is the data source that is passed in the constructor to initialize the state. They should've named it dataSource instead, that's the correct naming convention.
Hope that helped :)
If don't understand HOC from their docs, just keep searching in other sources. There are hundreds if not thousands of sources that explain that concept. In programming you sometimes need to go through several sources for a concept or and idea to sink in properly. Good luck!
I am creating a react web app. My state in my parent component is an array of objects (one to many number of objects stored in this array...it could be any number of objects). I want to send object X of the array through to my child component through props. Here is my child component:
import React, { Component } from 'react'
export class Card extends Component {
render() {
console.log('in card');
console.log(this.props.newCard);
return (
<div>
<h2>Here is a card!</h2>
</div>
)
}
}
export default Card
For necessary context, here is my render method in the parent component that calls the child (named Card):
render() {
return (
<div>
<Card newCard={this.state.cardList[this.state.eachCard]}></Card>
<button>Next</button>
</div>
)
}
The this.state.eachCard is just referring to an accumulator I will later implement to go through each object of the array upon clicking the Next button. Right now it is just set to position 0 for testing purposes.
When I console.log the newCard prop in the child component, this is the structure of the object that is send from parent to child:
{CardID: 3, CardName: "test", CardDefinition: "testing", category_CategoryID: 2}
However, I am wanting to specify a particular property of this object. For example, I want to retrieve the name of the card. However, when I tried to console.log this
console.log(this.props.newCard.CardName);
I received the following error:
"Cannot read property 'CardName' of undefined"
This does not make sense to me, as this.props.newCard was not undefined. Therefore it would make sense to me that specifying the newCard prop one more degree to newCard.CardName should logically work. I cannot figure out what I am missing. Is this some sort of syntax error? Or is my logic just totally off?
I seem to be very close, but am hung up on how to proceed...any ideas sure would be appreciated. Thanks!!
A good first step would be to guard against undefined here. I'm not sure what the rest of your code looks like but if there's some async happening somewhere it's possible on first render that the prop is undefined. When you pass undefined in to console.log it doesn't log anything so if this component is indeed getting rendered twice then you'd get no log for the first render. A great way to test this theory is to do your console log like the following:
console.log('newCard', this.props.newCard);
You can also guard against undefined here so it won't throw an error by returning null if this.props.newCard is in fact undefined.
export class Card extends Component {
render() {
if (this.props.newCard === undefined) {
return null;
}
return (
<div>
<h2>Here is a card!</h2>
</div>
)
}
}
export default Card
Edit due to additional context.
The way you render items in an array as children in react is using the map method of the array object and passing in a component to the callback:
return (
<div>
{
this.state.cardList.map(eachCard => (<Card newCard={eachCard} />))
}
<button>Next</button>
</div>
)
}
There is a typo in your console log, change porps by props.
I'm working on the freeCodeCamp drum machine app. In my app with function arrow components, I set state of display with the useState hook in the parent component and pass it as a prop to the child component. In the parent component, I try to render the display state in a div. However, when the method is triggered (on click of the "drum pad" div), the app crashes. In the console I get an error that says "Uncaught Invariant Violation: Objects are not valid as a React child (found: object with keys {display}). If you meant to render a collection of children, use an array instead."
I've been following along a YouTube tutorial for this project but using arrow function components and Hooks instead of regular classes as used in the tutorial--in the tutorial (around 1:55 of this video) the person successfully does what I'm trying to do, so I think the issue is something to do with using Hooks or arrow function components.
// APP COMPONENT (PARENT)
const sounds = [
{ id: 'snare', letter: 'Q', src: 'https://www.myinstants.com/media/sounds/snare.mp3' },
// etc.
];
const App = () => {
const [display, setDisplay] = useState(''); // <----
const handleDisplay = display => { // <----
setDisplay({ display });
}
return (
<div className="App">
<div className="drum-machine">
<div className="display">
<p>{display}</p> // <---- Related to error in console
</div>
<div className="drum-pads">
{sounds.map(sound => (
<DrumPad
id={sound.id}
letter={sound.letter}
src={sound.src}
handleDisplay={handleDisplay} // <----
/>
))}
</div>
</div>
</div>
);
}
// DRUMPAD COMPONENT (CHILD)
const DrumPad = ({ id, letter, src, handleDisplay }) => {
let audio = React.createRef();
const handleClick = () => {
audio.current.play();
audio.current.currentTime = 0;
handleDisplay(id); // <----
}
return (
<div
className="drum-pad"
id={id}
onClick={handleClick}
>
<p className="letter">{letter}</p>
<audio
ref={audio}
id={letter}
src={src}
>
</audio>
</div>
);
}
You're setting the state as an object instead of a string. Remove the curly brackets around it.
const handleDisplay = display => {
setDisplay(display);
}
This was already answered, but since you are following a tutorial, I am assuming you are learning React and wanted to point a couple of things to help you :)
The incorrect use of state was pointed out, but just for clarification (and the reason I think you were using an object): in the "old" way, with Class components, the state used to be an object, and you needed to update it like an object. This example here shows that. With Hooks, you don't need to set the whole State object, only that specific state property. More info here.
Another point is, in your CodePen example at least, you were missing the import for useState. You either need to import it like this import { useState } from React or use it like this React.useState, since this is a separate module, not imported by default when you import React.
The last point is, when creating components using a loop (like your <DrumPad> with the map) you need to provide a "key" attribute. that will help React keep track of things that needs to be updated or rerendered.
O updated your code with those changes in this link, if you wanna see it working:
https://codesandbox.io/s/reverent-browser-zkum2
Good luck and hope you are enjoying React Hooks :)
Hi i have the following React-Redux based code snipped and dont understand the following line:
const List = connect(mapStateToProps)(ConnectedList);
I would understand that a function is assigned to the List constant if it would look like:
const List = connect(mapStateToProps);
But what effect has the (ConnectedList) in this statement and what is the technical name of that what happend?
Full Snipped:
import React from "react";
import { connect } from "react-redux";
const mapStateToProps = state => {
return { articles: state.articles };
};
const ConnectedList = ({ articles }) => (
<ul className="list-group list-group-flush">
{articles.map(el => (
<li className="list-group-item" key={el.id}>
{el.title}
</li>
))}
</ul>
);
const List = connect(mapStateToProps)(ConnectedList);
export default List;
connect(...) returns a function (as you already realized), so connect(...)(ConnectedList) calls the function returned by connect() with ConnectedList as its argument.
The longer version of this would be:
const tmp = connect(mapStateToProps);
const List = tmp(ConnectedList);
Since you asked for the technical name: Usually a function returning another function is called a higher-order function.
connect(mapStateToProps);
Returns a high order React component(HOC). In this case, connect will inject the state you are mapping on the mapStateToProps pure function.
The purpose of a HOC is to compose another component, that's why you need the second part:
connect(mapStateToProps)(ConnectedList);
The HOC returned by connect() will add the props to the ConnectedList component.
You can see the docs here: connect documentation
connect() return an higher-order-component (component that wraps a component).
This function is responsible to subscribe to changes in your application's redux store, and whenever a change in store detected, it will call the mapStateToProps function you provided, passing the new store state to that function.
The value returned from mapStateToProps will then be pass to the component you are wrapping as props.
This makes the components connected to the redux store, and hence the name of it.
By the way, I would name the component you are wrapping as List, and the component returned from the connect() function as the ConnectedList.
The docs for React state that component functions can be accessed by a parent component via refs. See: https://facebook.github.io/react/tips/expose-component-functions.html
I am attempting to use this in my application but run into an "undefined is not a function" error when the child function is called. I'm wondering if this has anything to do with using the ES6 format for React classes because I don't see any other differences between my code and the docs.
I have a Dialog component that looks like the following pseudocode. The Dialog has a "Save" button that calls save(), which needs to call the save() function in the child Content component. The Content component collects information from child form fields and performs the save.
class MyDialog extends React.Component {
save() {
this.refs.content.save(); <-- save() is undefined
}
render() {
return (
<Dialog action={this.save.bind(this)}>
<Content ref="content"/>
</Dialog>);
}
}
class Content extends React.Component {
save() {
// Get values from child fields
// and save the content
}
}
I could instead pass a prop (saveOnNextUpdate) down to Content and then execute save whenever it is true, but I would rather figure out how to get the method detailed in the React doc above to work.
Any ideas on how to get the doc approach to work or access the child component function in a different way?
Redux connect accepts an option parametre as the forth parameter. In this option parameter you can set the flag withRef to true. Then you can access functions to refs by using getWrappedInstance(). Like this:
class MyDialog extends React.Component {
save() {
this.refs.content.getWrappedInstance().save();
}
render() {
return (
<Dialog action={this.save.bind(this)}>
<Content ref="content"/>
</Dialog>);
}
}
class Content extends React.Component {
save() { ... }
}
function mapStateToProps(state) { ... }
module.exports = connect(mapStateToProps, null, null, { withRef: true })(Content);
Read more about it here: https://github.com/reactjs/react-redux/blob/master/docs/api.md#connectmapstatetoprops-mapdispatchtoprops-mergeprops-options
Worth reading this article about use of refs and consider if there's better approaches: https://facebook.github.io/react/docs/refs-and-the-dom.html#dont-overuse-refs
An alternative way to do this would be to use some other prop name (other than ref). I've found that this also works well if you're using a library like styled-components or emotion For example in a connected MyComponent:
<MyComponent
...
innerRef={(node) => { this.myRef = node; }}
/>
As it turns out, m90 was right -- this was a different issue entirely. I'm posting the solution in case someone runs into the same problem in the future.
My application is built with Redux, and the problem stems from using the react-redux connect function to connect a component to the store/global state. For some reason, exporting a component and connecting it to the store makes it impossible to access the functions inside of it. In order to get around this, I had to remove all use of global state from Content so that I could export it as a "dumb" component.
To be more clear, Content.js looked like this:
var connect = require('react-redux').connect;
class Content extends React.Component {
save() {
// Get values from child fields
// and save the content
// Use of this.props.stateObject
}
}
function mapStateToProps(state) {
const {
stateObject
} = state;
return {
stateObject
};
}
module.exports = connect(mapStateToProps)(Content);
Removing the use of global state (and therefore the use of connect and mapStateToProps allowed me to export the component using:
module.exports = Content;
Accessing this.refs.content.save() magically worked after doing this.