import { Router, Route, IndexRoute, browserHistory } from 'react-router'
const router = (
<Router history={browserHistory}>
<Route path='/' component={Main}>
<IndexRoute component={} />
<Route path='/view/:postId' />
</Route>
</Router>
)
render(router, document.getElementById('root'))
<Link to=''>Bla</Link>
to create push state links.history
which first syncs the browser history with the react store (see next video).it is called store
import { createStore, compose } from 'redux'
import { syncHistoryWithStore } from 'react-router-redux'
import { browserHistory } from 'react-router'
import rootReducer from './reducers/index'
const store = createStore(rootReducer, defaultState)
export const history = syncHistoryWithStore(browserHistory, store)
export default store
action creators are functions which return actions:
function increment(index) {
return {
type: 'INCREMENT_LIKES',
index,
}
}
function addComment(postId, author, comment) {
return {
type: 'ADD_COMMENT',
postId,
author,
comment
}
}
always start like this and then add some conditional functionality:
function posts(state = [], action) {
if (...) {
return nextState
}
return state
}
export default posts
combine them in a root reducer
import { combineReducers } from 'redux'
import { routerReducer } from 'react-router-redux'
import posts from './posts'
const rootReducer = combineReducers({ posts, ..., routing: routerReducer })
export default rootReducer
always add routing: routerReducer
at the end to integrate the route changes with other action changes.
executes URL changes implicitely
replace the browserHistory as mentioned before.
import { Router, Route, IndexRoute, browserHistory } from 'react-router'
+import { Provider } from 'react-redux'
+import store, { history } from './store'
const router = (
+ <Provider store={store}>
- <Router history={browserHistory}>
+ <Router history={history}>
<Route path='/' component={Main}>
<IndexRoute component={} \/>
<Route path='/view/:postId' \/>
<\/Route>
<\/Router>
+ </Provider>
)
render(router, document.getElementById('root'))
The state is now stored in the store. $r.store.getState()
the store provides a dispatch function
// on the console
$r.store.dispatch({type: 'INCREMENT', index: 1})
The dispatcher sends the action to every reducer
Therefore, the reducer needs to have some conditional logic.
connect
injects the data in the components that need itfor that, we wrap the main component with one that has the state and dispatcher propagated
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import * as actionCreators from './actions/actionCreators'
import Main from './Main'
function mapStateToProps(state) {
return {
posts: state.posts,
comments: state.comments,
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators(actionCreators, dispatch)
}
const App = connect(mapStateToProps, mapDispatchToProps)(Main)
export default App
because Main ist just JSX, connect injects standardized props into it and makes the state and dispatcher available.
You can still propagate data down with props
Once you propagate the reducer function to the component, this is how you pass arguments to the function call
Remember that <button onClick={this.props.function(argument)} ...>
would call the function at document load!
<button onClick={this.props.function.bind(null, argument)} ...>
Use pure functions to manipulate state. So the same inputs (previous state, action) always gives the same output (next state).
Do the same as in React state handling: Copy, modify, return copy:
function posts(state = [], action) {
switch(action.type) {
const i = action.index
case 'INCREMENT_LIKES' :
return [
...state.slice(0, i),
{ ...state[i], likes: state[i].likes + 1 },
...state.slice(i + 1)
]
default:
return state
}
}
You can use findIndex()
to get a certain element of an array
render () {
const { postId } = this.props.params
const i = this.props.posts.findIndex((post) => post.code === postId)
const post = this.props.posts[i]
const comments = this.props.comments[postId] || []
...
}
Write a functions, which handles the submit of a form
...
handleSubmit(e) {
e.preventDefault()
const { postId } = this.props.params
const author = this.refs.author.value
this.props.addComment(postId, author, ...)
}
render() {
...
<form ref='commentForm' className='comment-form' onSubmit={this.handleSubmit}>
<input type='text' ref='author' placeholder='author'/>
...
}
Example for comments:
write other functions to only handle one comment at a time
function postComments(state = [], action) {
switch (action.type) {
case 'ADD_COMMENT':
return [
...state, {
user: action.author,
text: action.comment,
}
]
case 'REMOVE_COMMENT':
return [
...state.slice(0, action.index),
...state.slice(action.index + 1)
]
default:
return state
}
}
function comments(state = [], action) {
if (typeof action.postId !== 'undefined') {
return {
...state,
[action.postId]: postComments(state[action.postId], action)
}
}
return state
}
export default comment
Recall slices, spreads.
Think about what needs to get passed. This makes the code faster.
if you want that, add the following code to store.js
if (module.hot) {
module.hot.accept('./reducers/', () => {
const nextRootReducer = require('./reducers/index').default
store.replaceReducer(nextRootReducer)
})
}
To make use of the Redux dev tools (for chrome), you need to announce your store to the browser:
// index.js
const enhancers = compose(
window.devToolsExtension ? window.devToolsExtension() : f => f
)
const store = createStore(rootReducer, defaultState, enhancers)