I'm trying to pass props to my children components which are under the controller. I'm using redux and react-router for navigation. The problem is that everything is fine in the controller, it gets its initial props, but when I'm passing them to a child I have an undefined either the constructor or in render function. Here is my code:
import React, { Component } from 'react'
import { connect } from 'react-redux'
import moment from 'moment'
import LawsuitSchedule from '../components/LawsuitSchedule'
import LawsuitCalendar from '../components/LawsuitCalendar'
class LawsuitScheduleContainer extends Component {
constructor (props) {
super(props)
}
render () {
let {schedule} = this.props
console.log(schedule)
return (
<LawsuitCalendar initialDate={schedule.initialDate}
selectedDate={schedule.selectedDate}
scheduledDates={schedule.scheduledDates}/>,
<LawsuitSchedule/>
)
}
}
const mapStateToProps = state => ({
schedule: state.schedule,
})
export default connect(mapStateToProps)(LawsuitScheduleContainer)
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import momentPropTypes from 'react-moment-proptypes'
import moment from 'moment'
import DatePicker from 'react-datepicker'
import 'react-datepicker/dist/react-datepicker.css'
import '../styles/lawsuitCalendar.css'
export default class LawsuitCalendar extends Component {
constructor (props) {
super(props)
console.log(props)
}
propTypes = {
selectedDate: momentPropTypes.momentString,
scheduledDates: PropTypes.array.isRequired,
}
handleChange = (date) => {
this.setState({
selectedDate: date,
})
}
render () {
let {selectedDate, scheduledDates} = this.props
let highlightWithRanges = [
{
'scheduled-date': scheduledDates,
},
]
return (
<DatePicker
inline
selected={selectedDate}
onChange={this.handleChange}
highlightDates={highlightWithRanges}
/>
)
}
}
The problem was in another component, which renders LawsuitCalendar too. I have no clue why it was displayed only once, but I have solved my problem with props -- I just didn't pass them. I've added it, maybe someone can answer why has that happened?
import React, { Component } from 'react'
import PropTypes from 'prop-types'
import { Grid, Row, Col } from 'react-bootstrap'
import { Link } from 'react-router-dom'
import ReactTable from 'react-table'
import matchSorter from 'match-sorter'
import LawsuitCalendar from '../components/LawsuitCalendar'
import '../styles/lawsuitGrid.css'
import data from '../utils/data.json'
export default class LawsuitSchedule extends Component {
render () {
return (
<Grid>
<Row className="show-grid">
<Col md={4}><LawsuitCalendar/></Col>
<Col md={8}>
<ReactTable data={data}
...
/>
</Col>
</Row>
</Grid>
)
}
}
Related
I am currently having a problem getting store to be passed in as a prop and am wondering what to label a few things.
The current error is within create store, I'm unsure what to do with it.
I have tried other methods and only want to use the store method where I pass it in as a prop
import React from 'react';
import { MockGit } from './Constants';
import ExpansionPanelSummary from '#material-ui/core/ExpansionPanelSummary';
import ExpansionPanelDetails from '#material-ui/core/ExpansionPanelDetails';
import Typography from '#material-ui/core/Typography';
import ExpandMoreIcon from '#material-ui/icons/ExpandMore';
import ExpansionPanel from '#material-ui/core/ExpansionPanel';
import Button from '#material-ui/core/Button';
import TestAPI from './TestAPI';
import { displayGitData, userInfoURL, getDataSaga } from '../sagas/sagas';
import { createStore } from 'redux';
class GitData extends React.Component {
constructor(props) {
super(props);
}
render() {
const store = createStore(...); //this is what im unsure of.
const { store } = this.props;
return (
<ExpansionPanel>
<ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>
<Typography> {MockGit} </Typography>
</ExpansionPanelSummary>
<ExpansionPanelDetails>
{displayGitData()}
{userInfoURL()}
{getDataSaga()}
<TestAPI />
</ExpansionPanelDetails>
</ExpansionPanel>
);
}
}
export default GitData;
The goal is to get store passed in as a prop with no errors.
Any help would be great, Thanks!
You're doing it wrong, here's the recommended way to use React with Redux:
store.js
import { createStore } from 'redux';
export default createStore(...)
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store.js'
const App = () => (<h1>Hello from App</h1>);
ReactDOM.render(
<Provider store={store}><App/></Provider>
document.querySelector('#react-root')
);
You now have an app that is bound with the store.
The react-redux npm package allows also to bind component props to store dispatches and store state, example:
my-component.js
import React from 'react';
import { connect } from 'react-redux';
class MyComponent extends React.Component {
render() {
return (
<p>{this.props.hello}</p>
)
}
}
export default connect(state => ({hello: state.helloReducer.value}))(MyComponent)
For further tutorials, check the official docs of react-redux, or this good youtube playlist.
I know that the following snippet gets optimized if styles are created by Stylesheet.create() and Child extends PureComponent.
import React, { Component } from 'react'
import { View } from 'react-native'
import Child from './Child'
import styles from './stylesheets'
export default class Parent extends Component {
render() {
return (
<View>
<Child style={styles.x} />
</View>
)
}
}
Does it also works with multiple styles? I'm afraid that [] literals prevent optimization.
import React, { Component } from 'react'
import { View } from 'react-native'
import Child from './Child'
import styles from './stylesheets'
export default class Parent extends Component {
render() {
return (
<View>
<Child style={[styles.x, styles.y]} />
</View>
)
}
}
Or do I need to define combined style statically?
import React, { Component } from 'react'
import { View } from 'react-native'
import Child from './Child'
import styles from './stylesheets'
const childStyles = [styles.x, styles.y]
export default class Parent extends Component {
render() {
return (
<View>
<Child style={childStyles} />
</View>
)
}
}
I confirmed that array literal spoils PureComponent optimization.
I need to slightly change the theme depending on the current section of the site.
It seems that MuiThemeProvider only sets muiTheme on load; but it needs to be updated when the props change.
How can this be done?
You can try to put the theme in a wrapping component that keeps the theme in it's state. Using React's context this component exposes a function to child components to change the state.
import React, { Component } from 'react';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import PropTypes from 'prop-types';
import theme from './theme';
import themeOther from './theme-other'
class Wrapper extends Component {
static childContextTypes = {
changeTheme: PropTypes.func
};
constructor(props) {
super(props);
this.state = {
muiTheme: getMuiTheme(theme)
};
}
getChildContext() {
return {changeTheme: this.changeTheme};
}
changeTheme = () => {
this.setState({
muiTheme: getMuiTheme(themeOther)
})
};
render() {
return (
<MuiThemeProvider muiTheme={this.state.muiTheme}>
{this.props.children}
</MuiThemeProvider>
)
}
}
export default Wrapper;
In some child component you can access the context and call the changeTheme function to set a different theme in the state. Make sure to include contextTypes else you can't access the function.
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import PropTypes from 'prop-types'
class ChildComponent extends Component {
static contextTypes = {
changeTheme: PropTypes.func
};
render() {
return (
<RaisedButton
primary
onTouchTap={this.context.changeTheme}
label="Change The Theme"
/>
);
}
}
export default ChildComponent;
In the root of your app just render the wrapper.
ReactDOM.render(
<Wrapper>
<App />
</Wrapper>,
document.getElementById('root')
);
EDIT:
My first solution might have been abit too much. Since you are replacing the whole theme for the whole app. You can also use the MuiThemeProvider down the tree like so.
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import './index.css';
import inject from 'react-tap-event-plugin';
inject();
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import theme from './theme';
ReactDOM.render(
<MuiThemeProvider muiTheme={getMuiTheme(theme)}>
<App />
</MuiThemeProvider>,
document.getElementById('root')
);
In a child component you can just use the MuiThemeProvider again and override some properties. Note that these changes will reflect on all the children inside this MuiThemeProvider.
import React, { Component } from 'react';
import RaisedButton from 'material-ui/RaisedButton';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import theme from './theme';
import { green800, green900 } from 'material-ui/styles/colors';
const localTheme = getMuiTheme(Object.assign({}, theme, {
palette: {
primary1Color: green800,
primary2Color: green900
}
}));
class App extends Component {
render() {
return (
<div>
<RaisedButton
primary
label="Click"
/>
<MuiThemeProvider muiTheme={localTheme}>
<RaisedButton
primary
label="This button is now green"
/>
</MuiThemeProvider>
</div>
);
}
}
export default App;
I have a parent component and a child component, I want to pass
property from Parent to Child by using {...this.props}, I dont want
any action or reducer in the picture,Is it possible to do this?
My Child Component is like this:-
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class SampleChild extends Component {
constructor(props) {
super(props)
}
render(){
return(
<div>This is Parent</div>
)
}
}
SampleChild.propTypes={
Header:React.PropTypes.string.isRequired
}
export default SampleChild
My Parent Component is like this:-
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class SampleParent extends Component {
constructor(props) {
super(props)
}
render(){
return(
<div><SampleChild {...this.props}/></div>
)
}
}
export default SampleParent
Now how can I pass the Header Property from the SampleParent Component to SampleChild?.Please assist me.
<SampleParent Header="Hello from Parent" />
Will do the trick for you since you're spreading all props coming from SampleParent to SampleChild you need to make sure that the SampleParent just receives it as a prop if it's dynamic.
If it's a static prop you can define it in defaultProps for the SampleParent and you'll always pass the same string.
SampleParent.defaultProps = {
Header: 'Hello from Parent'
}
If you are just trying to pass "all" props from parent to child, you can do it this way.
From the component that is rendering the SampleParent ...
<SampleParent />
The SampleParent component:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import SampleChild from './SampleChild';
class SampleParent extends Component {
render() {
return(
<div>
<SampleChild {...this.props} />
</div>
)
}
}
SampleParent.defaultProps = {
Header: "Header from parent"
}
export default SampleParent;
The SampleChild component:
import React, { Component } from 'react'
import ReactDOM from 'react-dom'
class SampleChild extends Component {
render() {
return(
<div>
<div>This is the Header passed from parent:</div>
{this.props.Header}
</div>
)
}
}
SampleChild.propTypes = {
Header: React.PropTypes.string.isRequired
}
export default SampleChild;
I'm trying to refactor my app to separate presentational and container components. My container components are just the presentational components wrapped in connect() calls from react-redux, which map state and action creators to the presentational components' props.
todo-list.container.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {fetchTodos} from '../actions/todo.actions';
import TodoList from '../components/todo-list.component';
export default connect(({todo}) => ({state: {todo}}), {fetchTodos})(TodoList);
todo-list.component.jsx
import React, {Component} from 'react';
import TodoContainer from '../containers/todo.container';
export default class TodoList extends Component {
componentDidMount () {
this.props.fetchTodos();
}
render () {
const todoState = this.props.state.todo;
return (
<ul className="list-unstyled todo-list">
{todoState.order.map(id => {
const todo = todoState.todos[id];
return <li key={todo.id}><TodoContainer todo={todo} /></li>;
})}
</ul>
);
}
};
todo.container.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
import {createTodo, updateTodo, deleteTodo} from '../actions/todo.actions';
import Todo from '../components/todo.component';
export default connect(null, {createTodo, updateTodo, deleteTodo})(Todo);
todo.component.jsx
import React, {Component} from 'react';
import '../styles/todo.component.css';
export default class Todo extends Component {
render () {
return (
<div className="todo">
{todo.description}
</div>
);
}
};
What I'm trying to figure out is this: I know I should not be embedding the <TodoContainer /> element inside of TodoList because TodoList is a presentational component and it should only nest other presentational components inside of it. But if I replace it with just a <Todo /> presentational component, then I have to map every state prop and action creator prop in TodoListContainer that the Todo component would need and pass them all down the chain manually as props. This is something I want to avoid of course, especially if I start nesting more levels or start depending on more props coming from Redux.
Am I approaching this correctly? It seems that I shouldn't be trying to embed a container component inside of a presentational component in general, because if I can decouple presentational components from Redux, they become more reusable. At the same time, I don't know how else to embed a component that requires access to Redux state/dispatch inside of any other component that has markup.
To specifically answer your question: It is okay to nest presentational and container components. After all, they are all just components. In the interest of easy testing however, I would prefer nesting presentational components over container components. It all comes down to a clear structuring of your components. I find that starting in a single file and then slowly component-izing works well.
Have a look at nesting children and utilizing this.props.children to wrap child elements in a presentational component.
Example (removed some code for brevity)
List (presentational component)
import React, { Component, PropTypes } from 'react';
export default class List extends Component {
static propTypes = {
children: PropTypes.node
}
render () {
return (
<div className="generic-list-markup">
{this.props.children} <----- wrapping all children
</div>
);
}
}
Todo (presentational component)
import React, { Component, PropTypes } from 'react';
export default class Todo extends Component {
static propTypes = {
description: PropTypes.string.isRequired
}
render () {
return (
<div className="generic-list-markup">
{this.props.description}
</div>
);
}
}
TodoList (container component)
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import { createTodo, updateTodo, deleteTodo } from 'actions';
import List from 'components/List';
import Todo from 'components/Todo';
export class TodoList extends Component {
static propTypes = {
todos: PropTypes.array.isRequired,
create: PropTypes.func.isRequired
}
render () {
return (
<div>
<List> <---------- using our presentational component
{this.props.todos.map((todo, key) =>
<Todo key={key} description={todo.description} />)}
</List>
<a href="#" onClick={this.props.create}>Add Todo</a>
</div>
);
}
}
const stateToProps = state => ({
todos: state.todos
});
const dispatchToProps = dispatch = ({
create: () => dispatch(createTodo())
});
export default connect(stateToProps, dispatchToProps)(TodoList);
DashboardView (presentational component)
import React, { Component } from 'react';
import TodoList from 'containers/TodoList';
export default class DashboardView extends Component {
render () {
return (
<div>
<TodoList />
</div>
);
}
};