App component - set data from data.json, send props to children components:
import React from 'react';
import axios from 'axios';
import { HeaderPanel, PostsPanel } from './index';
import './app.scss';
export default class App extends React.Component {
constructor() {
super();
this.state = {
userData: Object,
infoData: Object,
postsData: Object
};
}
componentDidMount() {
axios.get('./data.json')
.then(res => {
this.setState({
userData: res.data.userData,
infoData: res.data.infoData,
postsData: res.data.posts
});
});
}
render() {
return (
<div className="app">
<div>
<HeaderPanel
userData={this.state.userData}
infoData={this.state.infoData}
/>
</div>
<div className="posts">
<PostsPanel postsData={this.state.postsData} />
</div>
</div>
);
}
}
First-child, divide props to approporiate components, give me the error too:
import React from 'react';
import PropTypes from 'prop-types';
import {ModalContainer, ModalDialog} from 'react-modal-dialog';
import { TopFunctionPanel, TopDisplayData } from './topPanel';
import { MiddleFunctionPanel, MiddleDisplayData } from './middlePanel';
import './headerPanel.scss';
export default class HeaderPanel extends React.Component {
constructor(props) {
super(props);
this.state = {
avatar: Object,
user: String,
city: String,
country: String,
like: Number,
following: Number,
followers: Number,
isOpen: false
};
this.setLike = this.setLike.bind(this);
this.setFollowers = this.setFollowers.bind(this);
this.toggleModalWindow = this.toggleModalWindow.bind(this);
}
componentWillReceiveProps(props) {
this.setState({
avatar : props.userData.avatar,
user: props.userData.user,
city: props.userData.city,
country: props.userData.country,
like : props.infoData.like,
following: props.infoData.following,
followers: props.infoData.followers,
});
}
setLike(val) {
this.setState({like: val});
}
setFollowers(val) {
this.setState({followers: val});
}
toggleModalWindow() {
this.setState({isOpen: !this.state.isOpen});
}
render() {
return (
<div className="headerPanel">
{
this.state.isOpen &&
<ModalContainer onClose={this.toggleModalWindow}>
<ModalDialog onClose={this.toggleModalWindow}>
<div className="modal">
<div>
<div><h1>You share:</h1></div>
</div>
<p>{window.location.href}</p>
</div>
</ModalDialog>
</ModalContainer>
}
<TopFunctionPanel
setLike={this.setLike}
like={this.state.like}
toggleModalWindow={this.toggleModalWindow}
/>
<TopDisplayData
avatar={this.state.avatar}
user={this.state.user}
city={this.state.city}
country={this.state.country}
/>
<MiddleDisplayData
like={this.state.like}
following={this.state.following}
followers={this.state.followers}
/>
<MiddleFunctionPanel
setFollowers={this.setFollowers}
followers={this.state.followers}
/>
</div>
);
}
}
HeaderPanel.propTypes = {
userData: PropTypes.object,
infoData: PropTypes.object
};
And grand-child. They give me this strange error too
import React from 'react';
import PropTypes from 'prop-types';
import './middlePanel.scss';
const DisplayData = ({ like, following, followers }) => {
return (
<div className="middleDisplayData">
<div>
<p>{like}</p>
<p>Liks</p>
</div>
<div>
<p>{following}</p>
<p>Following</p>
</div>
<div>
<p>{followers}</p>
<p>Followers</p>
</div>
</div>
);
};
export default DisplayData;
DisplayData.propTypes = {
like: PropTypes.number,
following: PropTypes.number,
followers: PropTypes.number
};
And I have a warning (console and jest gave me this):
warning.js:35 Warning: Failed prop type: Invalid prop like of type function supplied to DisplayData, expected number.
in DisplayData (created by HeaderPanel)
in HeaderPanel (created by App)
in div (created by App)
in div (created by App)
in App
I don't get it. My propTypes should be number (in grand-child component). But even if I change it to func, I have warning that it should be number. I know that with func I should to use .isRequire because of undefined, but here? I even have no idea what is wrong. I saw similar topics, but they's about func prop type (and as I understood adding .isRequire will help me) or some wrong spelling.
I updated my questione.
Hmm, it strange, but it looks like everything is ok. It looks like during sending props from one component to second they change they type. After render they are function and then consola show this warning. But in the end they change to number so, everything is ok.
Related
I'm trying to render the following the 'dogName' value of the following array to the browser, but it's coming up as 'undefined':
[
{
"id": 1,
"dogName": "bruce"
},
{
"id": 2,
"dogName": "borker"
},
{
"id": 3,
"dogName": "henry"
}
]
So, first of all, the data is pulled from a database and set in state in the parent component, where's it's passed as props to the child component 'DogNameList' (which I've trimmed down to just the relevant bits):
import React from 'react';
import './styles.css'
import DogList from './DogList'
import Dogue from './Dogue'
import axios from 'axios'
import DogNameList from './DogNameList'
class App extends React.Component {
constructor(){
super()
this.state = {
**dogName:[]**
}
}
componentDidMount() {
axios.get('http://localhost:3000/dogs')
.then(res => {
this.setState({
**dogName:res.data**
})
})
.catch(error => {
console.log(error)
})
}
render() {
return (
<div>
<DogNameList **names = {this.state.dogName}**/>
<Dogue/>
</div>
);
}
}
export default App;
In DogNameList, the data is mapped over and then passed as props to the 'Dogue' component (stupid names, I know, but this is a personal project):
import React from 'react'
import Dogue from './Dogue'
const DogNameList = (props) => {
return(
<div>
{
props.names.map(name => {
console.log(name.dogName)
return <Dogue name = {name} key ={name.id}/>
})
}
</div>
)
}
export default DogNameList
finally, it's supposed to be rendered to the browser via the 'Dogue' component:
import React from 'react'
import axios from 'axios'
class Dogue extends React.Component {
constructor(props){
super(props)
this.state = {
}
}
render(){
return (
<div>
<img className = 'img' src = {this.props.dogList}/>
<br/>
<form className = 'form'>
<input type = 'text' placeholder = 'Enter dog name'/>
<br/>
<button>Submit</button>
</form>
**<h2>dog name: {this.props.name}</h2>**
</div>
)
}
}
export default Dogue
Any ideas why it's not working? I console logged the following and it returned the list of names (not as strings, I should add):
props.names.map(name => {
console.log(name.dogName)
First of all, replace this
<h2>dog name: {this.props.name}</h2>
with this
<h2>dog name: {this.props.name.dogName}</h2>
because you are creating a component with object, so name property actually holds the object, not the name property of the object.
return <Dogue name = {name} key ={name.id}/>
You also don't declare somewhere this property
{this.props.dogList}
Also to handle the undefined error messages, do this
{this.state.dogName && <DogNameList names ={this.state.dogName}/>}
I'm trying to save card details for use later.
I have generated the SetupIntent client secret
I'm trying to use confirm card setup.
I'm following the docs here for react.
The following line:
const cardElement = this.props.elements.getElement('card')
is throwing me this error:
TypeError: Cannot read property 'getElement' of undefined
Where am I going wrong? My code is below:
This is the relevant portion of the main component:
import React from "react";
import { Elements, StripeProvider } from "react-stripe-elements";
import SaveCardForm from "./SaveCardForm";
<StripeProvider
apiKey={process.env.REACT_APP_API_STRIPE_PUBLISH}
>
<Elements>
<SaveCardForm/>
</Elements>
</StripeProvider>
And this is the SaveCardForm component
import React, { Component } from "react";
import { Stripe, CardElement, injectStripe } from "react-stripe-elements";
import axios from "axios";
class SaveCardForm extends Component {
constructor(props) {
super(props);
this.submit = this.submit.bind(this);
}
submit = e => {
e.preventDefault()
const cardElement = this.props.elements.getElement('card');
axios.get(`${process.env.REACT_APP_API}/saveCardDetails`).then(res => {
console.log('res.data', res.data)
this.props.stripe.confirmCardSetup(res.data.client_secret, {
payment_method: {
card: cardElement,
},
}).then( confirmCardSetupRes => {
console.log('confirmCardSetupRes', confirmCardSetupRes)
})
})
}
render() {
return (
<div>
<CardElement />
<button onClick={this.submit}>
Bid For Tickets
</button>
</div>
);
}
}
export default injectStripe(SaveCardForm);
Given your components, there is no prop named elements passed into SaveCardForm. If it's access to CardElement you are after, use a ref which will give you a direct reference to that component e.g.
constructor(props) {
...
this.cardEl = React.createRef();
}
submit = e => {
...
const card = this.cardEl.current.<accessDomHere>;
this.props.stripe.confirmCardSetup(res.data.client_secret, {
payment_method: {
card
},
}).then(...)
}
render() {
...
<div>
<CardElement ref={this.cardEl} />
...
</div>
}
Switch out <accessDomHere> for whatever DOM query you need to perform to get the information you need. There may even be a React property or function you can access (I'm not familiar with the component).
I resolved this by updating to the latest version of react-stripe-elements.
There is an error in the versions before 5.1.0
I have a TodoItem component and wants to import it in other component, this is the way i am importing:
import TodoItem from 'TodoItem';
what is wrong in this import line, it is throwing the error:
required is not defined
These are the components:
class LayOut extends React.Component{
constructor(){
super();
this.changeStatus = this.changeStatus.bind(this);
this.state = {
tasks:[
{
name:"buy milk",
completed: false
},
{
name:"buy water",
completed: false
},
{
name:"buy yougard",
completed: false
}
]
}
}
changeStatus(index){
var tasks = this.state.tasks;
var task = tasks[index];
task.completed = !task.completed;
this.setState({tasks:tasks})
}
render(){
return(
<ul>
{
this.state.tasks.map((task, index)=> {
return <TodoItem clickHandler={this.changeStatus} index={index} key={task.name} detail={task} />
})
}
</ul>
);
}
}
var app = document.getElementById('app');
ReactDOM.render(<LayOut />, app);
This is the file that i want to export:
class TodoItem extends React.Component{
render(){
return(
<li onClick={ ()=>{this.props.clickHandler(this.props.index); }} className={this.props.detail.completed ? 'completed' : ''}>
{this.props.detail.name}
</li>
);
}
}
export default TodoItem;
Use this: import TodoItem from './TodoItem'; //path to TodoItem
Reason: when you use import TodoItem from 'TodoItem', TodoItem will be treated as node modules, not a custom defined component. To correctly import the custom component we need to provide the path. use ./ if it exist in same directory, otherwise use ../ to provide the path.
Say I had a simple inputfieldcomponent like so:
import React, {PropTypes, PureComponent} from 'react';
import {render} from 'react-dom';
import {connect} from 'react-redux';
import updateInput from '../../actions/inputActions';
require('./SimpleInput.sass');
export class SimpleInput extends PureComponent {
constructor(props, context) {
super(props, context);
}
handleChange (event) {
this.props.updateField(event.target.value);
}
renderField () {
return (
<input type="text" value={this.props.value || ''} onChange={this::this.handleChange} placeholder={this.props.initial_value}/>
)
}
render () {
return(
<span>
{this.renderField()}
</span>
)
}
}
const mapStateToProps = (state) => {
return {value: state.value.value}
}
const mapDispatchToProps = (dispatch) => {
return {
updateInput: (value) => dispatch(updateInput(value))
};
};
AddressInput.propTypes = {
initial_value: PropTypes.string
};
AddressInput.defaultProps = {
initial_value: "What's the value?"
};
export default connect(mapStateToProps, mapDispatchToProps)(SimpleInput);
I then render two instances:
</SimpleInput initial_value='blah'/>
</SimpleInput>
However, when this is rendered, any update to one of the two fields updates both of them (due to redux only allowing for a single state).
What is the canonical way to approach this problem?
Here's one approach Abraham and giving an example to what Dan commented on. Check out this jsBin demonstrating the concept of a component (here it's SimpleInput) keeping its own internal state (such as a default placeholder) while still interacting with a Container (listening to onChange). While the example doesn't use Redux (for simplicity of creating the demo) you could easily substitute onChange handles with action dispatchers.
class SimpleInput extends React.Component {
constructor(props) {
super(props);
this.state = {
defaultPlaceholder: 'Default Placeholder'
}
}
render() {
return (
<input
value={this.props.value}
placeholder={this.props.placeholder || this.state.defaultPlaceholder}
onChange={this.props.onChange || ()=>{} }
/>
)
}
}
class SimpleInputContainer extends React.Component {
constructor(props) {
super(props);
this.changeInput1 = this.changeInput1.bind(this);
this.changeInput2 = this.changeInput2.bind(this);
this.state = {
input1: 'foo',
input2: 'bar',
}
}
changeInput1(e) {
this.setState({
input1: e.target.value
})
console.log(this.state);
}
changeInput2(e) {
this.setState({
input2: e.target.value
})
}
render() {
return (
<div>
<SimpleInput value={this.state.input1} onChange={this.changeInput1} />
<br />
<SimpleInput value={this.state.input2} onChange={this.changeInput2} />
<br />
<SimpleInput />
<br />
<SimpleInput placeholder={'Explicit Placeholder'} />
</div>
);
}
}
ReactDOM.render(<SimpleInputContainer />, document.getElementById('app'));
One of my use cases was an address lookup with an input managing it's value internally (no redux) and a lookup button emitting the value of the input via onClick={this.props.onSave} and in the Container handling all side effects/redux actions etc.
Lastly in your demo code, you're rerendering SimpleInput unnecessarily, which looks like a code smell. Hope that helps.
I ported redux-simple-router into a boilerplate react/redux isomorphic kit (https://github.com/erikras/react-redux-universal-hot-example). I have a simple click event handler that calls 'pushPath' from redux-simple-router. However, pushPath doesn't seem to update my URL. I already implemented the initial port (syncReduxAndRouter) and other routes seem to work fine (other routes use updatePath). Is there something else I need to do to get this to work?
import React, {Component} from 'react';
import { pushPath } from 'redux-simple-router';
import {connect} from 'react-redux';
#connect(null,
{ pushPath })
export default class MyContainer extends Component {
constructor() {
super();
this.state = { links: [{key: 0, name: 'Link1'}, {key: 1, name: 'Link2'}, {key: 2, name: 'Link3'}] };
}
// pass in redux actions as props
handleClick(value) {
pushPath('/Links/' + value);
}
render() {
return (
<div className="container">
<div>Search bar here</div>
<div className={styles.tile_container}>
Tiles here
{this.state.links.map(source =>
<div name={link.name} key={link.key} className={styles.source_tile} onClick= {this.handleClick.bind(this, link.name)}>{link.name}</div>
)}
</div>
</div>
);
}
}
Here's the version of my code with the fix. I needed to use an instance of redux-simple-router that was connected to the store and then pass its methods to the component as a prop.
import React, {Component, PropTypes} from 'react';
import { pushPath } from 'redux-simple-router';
import {connect} from 'react-redux';
#connect(null,
{ pushPath })
export default class MyComponent extends Component {
static propTypes = {
pushPath: PropTypes.func.isRequired
};
constructor() {
super();
this.state = { links: [{key: 0, name: 'Link1'}, {key: 1, name: 'Link2'}, {key: 2, name: 'Link3'}] };
}
// pass in redux actions as props
handleClick(value) {
this.props.pushPath('/Links/' + value);
}
render() {
return (
<div className="container">
<div>Search bar here</div>
<div className={styles.tile_container}>
Tiles here
{this.state.links.map(source =>
<div name={link.name} key={link.key} className={styles.source_tile} onClick= {this.handleClick.bind(this, link.name)}>{link.name}</div>
)}
</div>
</div>
);
}
}
You are calling action creator pushPath instead of the bound method.
Action creator just returns plain object. To call bound method, you should do
handleClick(value) {
this.props.pushValue('/Links/' + value);
}
#connect create proper methods for dispatching and propagate it to you via props.