Nesting a container component in a presentational component - reactjs

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>
);
}
};

Related

how can i use react redux in class component?

im a new to redux, and i want to know if it possible to use redux in class component.
we know that hooks works only in function component, so i export a function to use useSelector to access the store :
import { useSelector, useDispatch } from "react-redux";
export default function Selector() {
const counter = useSelector((state) => state.counter);
return counter;
}
and import it to the compone component so will be like this:
import React, { Component } from "react";
import selector from "../Store/selector";
export class Compone extends Component {
componentDidMount() {
console.log(selector());
}
render() {
return (
<>
<h1>
Counter: <span>0</span>
</h1>
<button style={{ marginRight: "10px" }}>Increase</button>
<button>Decrease</button>
</>
);
}
}
export default Compone;
and i import compone component to the App root component:
import React, { Component } from "react";
import Compone from "../components/compone/Compone";
export class App extends Component {
render() {
return (
<div>
<Compone />
</div>
);
}
}
export default App;
so this didn't work for me, is there a way to use redux in class component?,
how to use redux in class component,
how to use react redux in class component,
First of all you have to create some actions and some reducers.
Then you need to create a store using the reducers.
After that you can use connect method of react-redux with your class component.

Children component gets props as undefined

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>
)
}
}

How to define property in one component and pass to other component in reactJs?

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;

reactjs mobx without decorators not working

I am trying to incorporate mobx with react. Since I spawned my application using create-react-app, I can't use decorators given by mobx.
Given that we can use mobx without decorators as per this documentation: https://mobxjs.github.io/mobx/best/decorators.html
Here's a component that I created:
import React, { Component } from 'react';
import observer from 'mobx-react';
export const TestComponent = observer(class TestComponent extends Component {
render() {
return <div>Just a test component!</div>
}
});
Here's a simple calling of the above component:
import React, { Component } from 'react';
import './App.css';
import Auth from './Auth'
import { TestComponent } from './Test'
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import AppBar from 'material-ui/AppBar';
import authStore from './stores/Store'
class App extends Component {
constructor(props) {
super(props);
this.state = {
}
}
render() {
return (
<div className="app">
<MuiThemeProvider>
<div>
<TestComponent store={authStore} />
</div>
</MuiThemeProvider>
</div>
);
}
}
export default App;
Now when I run the above component, I get error: Uncaught TypeError: (0 , _mobxReact2.default) is not a function(…) nothing get displays in console.
What am I doing wrong here?
Please use import {observer} from 'mobx-react';
N.B. note that decorators can be used with create-react-app by using custom-react-scripts, as explained here)

Redux trigger action from the component

I am enjoy learning redux and applying it to my current project. got a question in my mind. at the moment action is pass down from the props. Just wondering can i import action and call action? because i may have nested nested component keep pass down the props may not be ideal would nice to trigger action at deeply level without pass down the props down.
Any suggestions appreciated :)
import React, { PropTypes, Component } from 'react';
export default class ButtonGroup extends Component{
static PropTypes = {
actions: PropTypes.object.isRequired
};
render() {
return (
<button className="button" onClick={ e => { this.props.actions.popForm()}}>Create New</button>
);
}
}
In order to do what you're describing, you need to use connect from react-redux.
So, in your example:
import React, { PropTypes, Component } from 'react';
import { connect } from 'react-redux';
import * as actions from './actions';
export class ButtonGroup extends Component{
static PropTypes = {
popForm: PropTypes.function.isRequired
};
render() {
return (
<button className="button" onClick={ e => { this.props.popForm()}}>Create New</button>
);
}
}
export default connect(null, actions)(ButtonGroup);

Resources