I'm developing a course scheduling application in react, and I'm using it as a chance to learn redux. There's a browse menu (implemented as the Browse component) that I'm currently designing and it displays the list of schools in the university upon load. When a user clicks on one of the schools, it displays a list of subjects within that school. However, I would like the schools to be displayed as a grid (two columns), and I would like the subjects to be displayed as a list (one column). They also may presumably require different props to be fed to the Browse component.
How would I go about doing this? Currently, I have a Browse component and a BrowseReduxContainer component. The BrowseReduxContainer component uses connect(), mapStateToProps, and mapDispatchToProps from react-redux to populate the props of Browse. This works fine when I'm just displaying schools, but I'm not sure how to modify the layout of Browse depending upon the state. Should I be giving connect() different components depending on the state? Or should I implement logic within the Browse component to check the prop and display a grid/list accordingly? Or something else entirely?
actions.js
export function showSubjects(schoolId) {
return {
type: 'SHOW_SUBJECTS',
schoolId
};
}
browse.js
const initialState = {
currentView: 'schools',
schools: [{id: 'AAAA', name: 'aaaa'}, {id: 'BBBB', name: 'bbbb'}],
subjects: [{id: 'CCC', name: 'ccc'}, {id: 'DDD', name: 'ddd'}]
};
function browse(state = initialState, action) {
switch (action.type) {
case 'SHOW_SUBJECTS':
return {
...state,
currentView: 'subjects'
};
default:
return state;
}
}
export default browse;
BrowseReduxContainer.jsx
import { connect } from 'react-redux';
import { showSubjects } from '../actions/actions';
import Browse from '../components/Browse.jsx';
function propsFilter(state) {
switch (state.currentView) {
case 'schools':
return state.schools;
case 'subjects':
return state.subjects;
default:
throw new Error(`No such view: ${state.currentView}`);
}
}
const mapStateToProps = (state) => ({
schools: propsFilter(state)
});
const mapDispatchToProps = (dispatch) => ({
showSubjects: (schoolId) => {
dispatch(showSubjects(schoolId));
}
});
const BrowseReduxContainer = connect(mapStateToProps, mapDispatchToProps)(Browse);
export default BrowseReduxContainer;
Browse.jsx
import React from 'react';
import RaisedButton from 'material-ui/RaisedButton';
const Browse = (props) => (
<div>
{props.schools.map((school) => (
<RaisedButton key={school.id} label={school.name} onClick={props.showSubjects(school.id)} />
))}
</div>
);
export default Browse;
Other relevant files, if necessary, can be viewed here: https://github.com/Joonpark13/serif.nu/tree/feature/browse
UPDATE: My best guess at this point was to have different view components with their own appropriate props, perhaps called BrowseA and BrowseB and connect the appropriate one according to the state. I want to include this logic in mapDispatchToProps within BrowseReduxContainer, but then I realized the mapDispatchToProps function does not take state as a parameter. I'd love any suggestions!
connect
state on your Child componentsUsing
connect
on child components has the following advantages:Your parent component need not bother about
connect
ing all the props required by its children even though the parent itself is not using the prop.Child components become more reusable, and easily maintainable.
Avoids passing down props blindly from parent to children. If the
Child
requires a considerable number of props, people don't want to explicitly pass only the required props, instead they tend to do this inside the parent:<Child {...this.props} />
.Use
connect
and you know what your Component is getting.You don't have to repeat
propTypes
definitions in parent and children.Separate business logic from views:
Business logic is:
Views should:
Simplify UI components so that they are small, reusable, and easily maintainable
Get props through
connect
from a business logic function.What is a business logic function?
Business logic functions are small reusable functions that input data and output modified data. If they are small, they can be easily reused and modified. Business logic functions should be pure. Because business logic functions are often reused, they work best when memoized. In some languages these are called getters or selectors.
To streamline memoization, you may use reselect library. It’s a very simple as it only does two things: memoization and reusability. Take a look at the official API for more information on how it does that.
Advantages: