I have searched around, all questions are something about How to pass props to {this.props.children}
But my situation is different,
I fill App with a initial data -- nodes, and map nodes to a TreeNodelist, and I want each TreeNode has the property of passed in node.
Pseudo code:
App.render:
{nodes.map(node =>
<TreeNode key={node.name} info={node} />
)}
TreeNode.render:
const { actions, nodes, info } = this.props
return (
<a>{info.name}</a>
);
Seems node not be passed in as info, log shows info is undefined.
warning.js?8a56:45 Warning: Failed propType: Required prop `info` was not specified in `TreeNode`. Check the render method of `Connect(TreeNode)`.
TreeNode.js?10ab:57 Uncaught TypeError: Cannot read property 'name' of undefined
below just a more complete code relate to this question(store and action is not much relation I think):
containers/App.js:
import React, { Component, PropTypes } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import Footer from '../components/Footer';
import TreeNode from '../containers/TreeNode';
import Home from '../containers/Home';
import * as NodeActions from '../actions/NodeActions'
export default class App extends Component {
componentWillMount() {
// this will update the nodes on state
this.props.actions.getNodes();
}
render() {
const { nodes } = this.props
console.log(nodes)
return (
<div className="main-app-container">
<Home />
<div className="main-app-nav">Simple Redux Boilerplate</div>
<div>
{nodes.map(node =>
<TreeNode key={node.name} info={node} />
)}
</div>
<Footer />
</div>
);
}
}
function mapStateToProps(state) {
return {
nodes: state.opener.nodes
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(NodeActions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(App);
containers/TreeNode.js
import React, { Component, PropTypes } from 'react'
import { bindActionCreators } from 'redux'
import { connect } from 'react-redux'
import classNames from 'classnames/bind'
import * as NodeActions from '../actions/NodeActions'
class TreeNode extends Component {
handleClick() {
this.setState({ open: !this.state.open })
if (this.state.open){
this.actions.getNodes()
}
}
render() {
const { actions, nodes, info } = this.props
if (nodes) {
const children =<div>{nodes.map(node => <TreeNode info={node} />)}</div>
} else {
const children = <div>no open</div>
}
return (
<div className={classNames('tree-node', { 'open':this.props.open})} onClick={ () => {this.handleClick()} }>
<a>{info.name}</a>
{children}
</div>
);
}
}
TreeNode.propTypes = {
info:PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
}
function mapStateToProps(state) {
return {
open: state.open,
info: state.info,
nodes: state.nodes
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(NodeActions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TreeNode);
reducers/TreeNodeReducer.js
import { OPEN_NODE, CLOSE_NODE, GET_NODES } from '../constants/NodeActionTypes';
const initialState = {
open: false,
nodes: [],
info: {}
}
const testNodes = [
{name:'t1',type:'t1'},
{name:'t2',type:'t2'},
{name:'t3',type:'t3'},
]
function getFileList() {
return {
nodes: testNodes
}
}
export default function opener(state = initialState, action) {
switch (action.type) {
case OPEN_NODE:
var {nodes} = getFileList()
return {
...state,
open:true,
nodes:nodes
};
case CLOSE_NODE:
return {
...state,
open:false
};
case GET_NODES:
var {nodes} = getFileList()
return {
...state,
nodes:nodes
};
default:
return state;
}
}
For complete code, can see my github https://github.com/eromoe/simple-redux-boilerplate
This error make me very confuse. The sulotion I see are a parent already have some children, then feed props to them by using react.Children, and them don't use redux.
When looping on nodes values, you call TreeNode and give the property info: that is good!
But when your component is rendered, this function is called:
function mapStateToProps(state) {
return {
open: state.open,
info: state.info,
nodes: state.nodes
};
}
As you can see, the prop info will be overriden with the value in state.info. state.info value is undefined I think. So React warns you that TreeNode requires this value. This warning comes from your component configuration:
TreeNode.propTypes = {
info:PropTypes.object.isRequired
}
Why state.info is undefined? I think you doesn't call it as it should. You should call state['reducerNameSavedWhenCreatingReduxStore].infoto retreive{}`.
You shouldn't fill ThreeNode through both props & connect().
It's because you are rendering a Redux connected component from within a parent Redux connected component and trying to pass props into it as state.
Why does TreeNode.js need to be connected to Redux? Props/Actions should be passed uni-directionally with only the top level component connected to state and all child components being essentially dumb components.
TreeNode should look similar to this:
class TreeNode extends Component {
handleClick() {
this.setState({ open: !this.state.open })
if (this.state.open){
this.props.actions.getNodes();
}
}
render() {
const { nodes, info } = this.props
if (nodes) {
const children =<div>{nodes.map(node => <TreeNode info={node} />)}</div>
} else {
const children = <div>no open</div>
}
return (
<div className={classNames('tree-node', { 'open':this.props.open})} onClick={ () => {this.handleClick()} }>
<a>{info.name}</a>
{children}
<div>{nodes.map(node => <TreeNode info={node} />)}</div>
</div>
);
}
}
TreeNode.propTypes = {
info: PropTypes.object.isRequired,
actions: PropTypes.object.isRequired
}
export default class TreeNode;
and the parent component would render TreeNode like this, passing the props in to the component:
<div>
{nodes.map(node =>
<TreeNode key={node.name} info={node} actions={this.props.actions} />
)}
</div>
Related
I have a React App with a shopping cart component. I use Redux to update the shopping cart when clicking on a "Add to cart" button in an item. The problem is, even I update the props in the item component, the prop is not updating concurrently. When I'm checking the props in the component in the Chrom developer tools components tab, I can see the props are updating only when I navigate to another component. However, the cart component never receives the updated prop to populate the cart items. These are the necessary components.
Items component
import React, { Component } from 'react';
import ProductData from './DataSet/ProductData';
import { compose } from 'recompose';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { updateCartList } from '../../store/actions';
class LatestProducts extends Component {
addItemToCart = (id) => {
const { cartList, updateCartList } = this.props;
var items = cartList;
ProductData.forEach(each => {
if (each.id === id) {
items.push(each)
}
});
updateCartList(items);
}
render() {
return (
<div>
<div className="itemGridMain">
{
ProductData.map(product => {
return (
<div className="itemCard" key={product.id}>
<button onClick={() => this.addItemToCart(product.id)}>Add to cart</button>
</div>
)
})
}
</div>
</div>
);
}
}
function mapStateToProps(state) {
return {
cartList: state.cartList,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
updateCartList: updateCartList,
}, dispatch);
}
export default compose(connect(mapStateToProps, mapDispatchToProps))(LatestProducts);
Cart component
import React, { Component } from 'react';
import { compose } from 'recompose';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { updateCartList } from '../../store/actions';
class FrontHeader extends Component {
render() {
const { cartList } = this.props;
return (
<div className="cartList">
{
cartList && cartList.length > 0 && cartList.map(item => {
return (
<div className="listItem" key={item.id}>
</div>
)
})
}
</div>
);
}
}
function mapStateToProps(state) {
return {
cartList: state.cartList,
};
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({
updateCartList: updateCartList,
}, dispatch);
}
export default compose(connect(mapStateToProps, mapDispatchToProps))(FrontHeader);
Cart List Reducer
const cartListReducer = (state = [], action) => {
switch (action.type) {
case 'UPDATE_CARTLIST':
return action.payload;
default:
return state;
}
}
export default cartListReducer;
Cart List Index
import cartListReducer from './cartlist';
import { combineReducers } from 'redux';
const allReducers = combineReducers({
cartList: cartListReducer,
})
export default allReducers;
Redux Actions
export const updateCartList = (newCartList) => {
return {
type: 'UPDATE_CARTLIST',
payload: newCartList,
}
}
How can I solve this?
Issue
this.props.cartList is your state and by pushing into that array and saving it back into state you are simply mutating state.
addItemToCart = (id) => {
const { cartList, updateCartList } = this.props;
var items = cartList; // <-- state reference
ProductData.forEach(each => {
if (each.id === id) {
items.push(each) // <-- push into state reference
}
});
updateCartList(items); // <-- saved state reference
}
Solution
You should provide a new array object reference for react to pick up the difference since reconciliation uses shallow object equality.
Your addItemToCart should probably just take the item you want added to the cart and move the cart update logic to the reducer.
LatestProducts
class LatestProducts extends Component {
addItemToCart = (item) => {
const { updateCartList } = this.props;
updateCartList(item); // <-- pass item to action creator
}
render() {
return (
<div>
<div className="itemGridMain">
{ProductData.map(product => {
return (
<div className="itemCard" key={product.id}>
<button
onClick={() => this.addItemToCart(product)} // <-- pass product/item
>
Add to cart
</button>
</div>)
})
}
</div>
</div>
);
}
}
cartListReducer
const cartListReducer = (state = [], action) => {
switch (action.type) {
case 'UPDATE_CARTLIST':
return [...state, action.payload]; // <-- copy state and appen new item
default:
return state;
}
}
I've asked a similar-ish question here before, however my code has changed quite a bit and I can not figure this out. I am certain it's an issue with what I am passing to my action/reducer. I would seriously appreciate it if someone could explain what I am doing wrong here. I really want to get this, just having a hard time with it.
actions.js
import { ADD_TODO, REMOVE_TODO } from '../constants/action-types';
export const addTodo = (todo) => (
{
type: ADD_TODO,
payload: todo
}
);
export const removeTodo = (id) => (
{
type: REMOVE_TODO,
payload: id
}
)
reducers.js
import { ADD_TODO, REMOVE_TODO, ADD_OPTIONS } from '../constants/action-types';
import uuidv1 from 'uuid';
const initialState = {
todos: []
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case ADD_TODO:
return {
...state,
todos: [...state.todos,
{
title: action.payload.inputValue,
id: uuidv1(),
createdAt: Date(),
priority: '',
deadline: '',
isClicked: false
}]
}
case REMOVE_TODO:
return {
...state,
todos: [...state.todos.filter(todo => todo.id !== action.payload)]
}
case ADD_OPTIONS:
return {
...state,
todos: [...state.todos,
{
isClicked: false
}]
}
default:
return state;
}
}
export default rootReducer;
TodoList.js
import React, { Component } from 'react';
import TodoItem from './TodoItem';
import { removeTodo } from '../actions';
import { connect } from 'react-redux';
const mapDispatchToProps = dispatch => {
return {
removeTodo: id => dispatch(removeTodo(id))
};
};
const mapStateToProps = state => {
return {todos: [...state.todos]};
};
class List extends Component {
render() {
const mappedTodos = this.props.todos.map((todo, index) => (
<TodoItem
title={todo.title}
key={index}
removeTodo={this.props.removeTodo}
/>
));
return (
mappedTodos
);
}
}
const TodoList = connect(mapStateToProps, mapDispatchToProps) (List)
export default TodoList;
TodoItem.js
import React, { Component } from 'react';
import uuid from 'uuid';
import '../../css/Todo.css';
class TodoItem extends Component {
render() {
const todoId = uuid();
return (
<div id={todoId}>
{this.props.title}
<button onClick={this.props.removeTodo}>X</button>
</div>
);
}
}
export default TodoItem;
You need to wrap your remove handler in an expression that can be evaluated at "click time" and use the todo id from the closure:
class TodoItem extends Component {
render() {
const todoId = uuid();
return (
<div id={todoId}>
{this.props.title}
<button onClick={this.props.removeTodo}>X</button>
</div>
);
}
}
Should be something like...
class TodoItem extends Component {
render() {
const todoId = uuid();
return (
<div id={todoId}>
{this.props.title}
<button onClick={() => this.props.removeTodo(todoId)}>X</button>
</div>
);
}
}
Along the lines of what #The Dembinski was saying, it works when I change my TodoList component to look like this:
import React, { Component } from 'react';
import TodoItem from './TodoItem';
import { removeTodo } from '../actions';
import { connect } from 'react-redux';
const mapDispatchToProps = dispatch => {
return {
removeTodo: id => dispatch(removeTodo(id))
};
};
const mapStateToProps = state => {
return {todos: [...state.todos]};
};
class List extends Component {
render() {
const mappedTodos = this.props.todos.map((todo, index) => (
<TodoItem
title={todo.title}
key={index}
removeTodo={() => this.props.removeTodo(todo.id)}
/>
));
return (
mappedTodos
);
}
}
const TodoList = connect(mapStateToProps, mapDispatchToProps) (List)
export default TodoList;
Changing my removeTodo prop in the map here DID fix the issue and now deletes properly. However, if anyone could help me understand this better either by further discussion, or just by pointing my in the right direction as to what I should be researching. Would be greatly appreciated. I'm not after answers, I'm after learning.
I'm doing a simple redux / react todo app. I can't get the todo items to show up. I'm able to console.log the data, but can't get it to appear. What am I doing wrong?
I separated the files, here is my app.js:
import React, { Component } from 'react';
import Todos from './todos';
import TodoList from "./todo_list";
export default class App extends Component {
render() {
return (
<div>
<Todos />
<TodoList/>
</div>
);
}
}
Here is the container Todos:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { addTodo } from '../actions/index';
class Todos extends Component {
constructor(props) {
super(props);
this.state = {text: ''};
}
addTodo(e) {
e.preventDefault();
this.props.addTodo(this.state.text);
this.setState({
text: ''
});
}
updateValue(e) {
this.setState({text: e.target.value})
}
render() {
return (
<div>
<form onSubmit={(e) => this.addTodo(e)}>
<input
placeholder="Add Todo"
value={this.state.text}
onChange={(e) => {
this.updateValue(e)
}}
/>
<button type="submit">Add Todo</button>
</form>
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({addTodo}, dispatch);
}
export default connect(null, mapDispatchToProps)(Todos);
Here is the TodoList:
import React, {Component} from 'react';
import {connect} from 'react-redux';
class TodoList extends Component {
render() {
return (
<ul>
{ this.props.todo.map((tod) => {
return <li key={tod.message}>{ tod.message }</li>
})}
</ul>
);
}
}
function mapStateToProps({ todo }) {
console.log({ todo });
return { todo };
}
export default connect(mapStateToProps)(TodoList);
Reducer:
import { ADD_TODO } from '../actions/types';
export default function(state=[], action) {
switch(action.type) {
case ADD_TODO:
return [ action.payload.message, ...state ]
}
return state;
}
And action
import { ADD_TODO } from './types';
const uid = () => Math.random().toString(34).slice(2);
export function addTodo(message) {
const action = {
id: uid(),
message: message
};
return {
type: ADD_TODO,
payload: action
};
}
This is what I get from the console.log({todo});
Here is my reducers/index:
import { combineReducers } from 'redux';
import TodosReducer from './reducer_addTodo';
const rootReducer = combineReducers({
todo: TodosReducer
});
export default rootReducer;
It's because there's a disconnect between your TodoList and reducer. TodoList, when mapping, expects each todo to have a message prop, but your reducer, when returning next state, only includes the message in the state array, not an object with the message property:
case ADD_TODO:
return [ action.payload.message, ...state ]
Instead, do not just put the message string in the next state's array, put in the whole object:
case ADD_TODO:
return [ action.payload, ...state ]
Now every single element in the todo array will be an object and have a message and id property. Also, try using an always unique expression for key -- it really shouldn't be the todo message, nor the id you supplied because it's using Math.random which both have a possibility of keys being the same.
I have this autocomplete component that takes an array of terms as a dataSource prop. The data I want to feed in resides in a public API, and I've followed the tutorial here to get to the code below. But this tutorial (and many others out there) explain how to bind these actions to an event, whereas I want to populate this prop with data on page load. How would I go about doing that?
actions.js
import fetch from 'isomorphic-fetch';
export function loadSchools(termId) {
return {
type: 'LOAD_SCHOOLS',
termId
};
}
export function receiveSchools(termId, json) {
return {
type: 'RECEIVE_SCHOOLS',
termId,
schools: json.data.children.map(child => child.data), // ???
receivedAt: Date.now()
};
}
export function getSchools(termId) {
return function (dispatch) {
dispatch(loadSchools(termId));
return fetch('http://www.northwestern.edu/class-descriptions/4650/index-v2.json')
.then(response => {
if (response.status >= 400) {
throw new Error('Bad response from server');
}
return response.json();
})
.then(data => dispatch(receiveSchools(termId, data)));
};
}
reducers.js
const initialState = {
schoolsData: {
isFetching: false,
lastUpdated: 0,
schools: []
}
};
function schools(state = initialState, action) {
switch (action.type) {
case 'LOAD_SCHOOLS':
return {
...state,
isFetching: true
};
case 'RECEIVE_SCHOOLS':
return {
...state,
isFetching: false,
schools: action.schools,
lastUpdated: receivedAt
}
default:
return state;
}
}
export default schools;
Search.jsx
import React from 'react';
import AutoComplete from 'material-ui/AutoComplete';
export default class Search extends React.Component {
render() {
return (
<AutoComplete
hintText="Search for something."
dataSource={this.props.searchdata}
maxSearchResults={15}
filter={AutoComplete.caseInsensitiveFilter}
onNewRequest={}
/>
);
}
}
Search.propTypes = {
searchdata: React.PropTypes.array.isRequired,
onSelect: React.PropTypes.func
};
index.jsx
import 'babel-polyfill';
import React from 'react';
import ReactDOM from 'react-dom';
import getMuiTheme from 'material-ui/styles/getMuiTheme';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import { grey500, white, fullBlack } from 'material-ui/styles/colors';
import { fade } from 'material-ui/utils/colorManipulator';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import thunkMiddleware from 'redux-thunk';
import schools from './reducers/reducers';
import colors from './colors';
import NavBar from './components/NavBar.jsx';
import Serif from './components/Serif.jsx';
const store = createStore(schools, applyMiddleware(thunkMiddleware));
const muiTheme = getMuiTheme({
palette: {
primary1Color: colors.northwesternPurple,
primary2Color: colors.northwesternPurple120,
primary3Color: grey500,
accent1Color: colors.northwesternPurple30,
accent2Color: colors.richBlack10,
accent3Color: colors.richBlack50,
textColor: colors.richBlack80,
alternateTextColor: white,
canvasColor: white,
borderColor: colors.richBlack20,
disabledColor: fade(colors.richBlack80, 0.3),
pickerHeaderColor: colors.northwesternPurple,
clockCircleColor: fade(colors.richBlack80, 0.07),
shadowColor: fullBlack
}
});
class App extends React.Component {
render() {
return (
<Provider store={store}>
<MuiThemeProvider muiTheme={muiTheme}>
<div> {/* MuiThemeProvider requires stricly one child element */}
<NavBar />
<Serif /> {/* This component contains SearchContainer, which in turn contains Search */}
</div>
</MuiThemeProvider>
</Provider>
);
}
}
ReactDOM.render(<App />, document.getElementById('app'));
You can render your Search component from another component, let's call it SearchContainer. SearchContainer is decorated by the connect function from react-redux that has as only role to dispatch the action to fetch the schools. SearchContainer doesn't render Search component until the school are fetched.
Here an example of what the code would look like. Here I assume you don't use react-redux.
First you have a small problem in your initial state in reducers.js. It should be:
const initialState = {
isFetching: false,
lastUpdated: 0,
schools: []
};
function schools(state = initialState, action) {
switch (action.type) {
case 'LOAD_SCHOOLS':
return {
...state,
isFetching: true
};
case 'RECEIVE_SCHOOLS':
return {
...state,
isFetching: false,
schools: action.schools,
lastUpdated: receivedAt
}
default:
return state;
}
}
SearchContainer.js
// ./containers/SearchContainer.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadSchools } from '../actions/actions'
import Search from '../components/Search';
class SearchContainer extends Component {
componentDidMount() {
this.props.loadSchools(this.props.termId);
},
render() {
const {
schools,
isFetching
} = this.props;
if (isFetching) {
return null;
}
return <Search schools={schools} />;
}
}
const mapStateToProps = (state) => ({
isFetching: state.isFetching,
schools: state.schools
});
const mapActionsToProps = (dispatch) => ({
loadSchools: (termId) => dispatch(loadSchools(termId)),
});
export default connect(mapStateToProps, mapActionsToProps)(SearchContainer);
In this way, at the first render, your Search component is not rendered. It is rendered, only after the schools are loaded.
You can dispatch the LOAD_SCHOOLS action from the componentDidMount lifecycle method (maybe in your Serif component but I can't see the code for that).
From the docs:
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request. Setting state in this method will trigger a re-rendering.
https://facebook.github.io/react/docs/react-component.html#componentdidmount
I'm beginner in react and redux, I have action which posts JSON on API and then receives list, this action called from button click, this all process works good but after populating data ui is not updating
Action:
import * as types from './actionTypes'
import { postMessage } from '../api/messaging'
function postToAPI(msg, dispatch) {
dispatch({ type: types.MESSAGE_POSTING });
postMessage(msg, (messages) => {
dispatch({
type: types.MESSAGE_POST_DONE,
messages: messages
});
});
}
export function postMessageAction(msg) {
return (dispatch) => {
postToAPI(msg, dispatch);
}
}
Reducer:
import * as types from '../actions/actionTypes'
const initialState = {
messages: []
}
export default function messages(state = initialState, action) {
switch(action.type) {
case types.MESSAGE_POST_DONE:
return {
...state,
messages: action.messages
}
this.forceUpdate();
default:
return state;
}
}
Main container:
export default class App extends Component {
render() {
return (
<Provider store={store}>
<CounterApp />
</Provider>
);
}
}
CounterApp:
class CounterApp extends Component {
constructor(props) {
super(props);
}
render() {
const { state, actions } = this.props;
return (
<Messaging />
);
}
}
export default connect(state => ({
messages: state.default.messages.messages
}))(CounterApp);
Messaging:
class Messaging extends Component {
render() {
return (
<View>
<MessageList messages={this.props.messages} />
<Message />
</View>
)
}
}
export default connect(state => ({
messages: state.default.messages.messages
}))(Messaging);
Message list:
export default class MessageList extends Component {
constructor(props) {
super(props);
}
render() {
return (
<ScrollView>
{
this.props.messages.map((item, index) => {
return (
<Text>
{ item.body }
</Text>
)
})
}
</ScrollView>
)
}
}
My MessageList component does not updates when messages changed. I read difference between props and state but i dont know how to pass data to state.
Update:
My state in messaging connect looks like this why i used default
Any ideas?
Your code looks strange. Firstly you need to connect to redux only in one component "Messaging"
import { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
const mapStateToProps = state => ({
messages: state.messages.messages
});
#connect(mapStateToProps);
class Messaging extends Component {
static propTypes = {
messages: PropTypes.object
}
render() {
const { messages } = this.props;
return (
<View>
<MessageList messages={messages} />
<Message />
</View>
)
}
}
Then use MessageList like dumb component to receive and render data.
export default class MessageList extends Component {
constructor(props) {
super(props);
}
renderMessages(item, index) {
return <Text>{item.body}</Text>;
}
render() {
const { messages } = this.props;
return (
<ScrollView>
{messages.map((item, index) => this.renderMessages(item, index))}
</ScrollView>
);
}
}
At a guess I'd say your connect statement wants to be
messages: state.messages
rather than
messages: state.default.messages.messages.
Also from what I can see I don't think you need the connect statement in CounterApp, it's not doing anything.
I'm not sure if the returned messages should replace or be merged with the existing messages but your reducer should be either
case types.MESSAGE_POST_DONE:
return {
messages: action.messages
}
if it's replacing the existing list or
case types.MESSAGE_POST_DONE:
return {
messages: [...state.messages, ...action.messages]
}
if you want to merge them.
A few things I noticed are:
From what I can see there's no default object in the state (you wrote messages: state.default.messages.messages).
You shouldn't use forceUpdate() in your reducer.
While it won't break anything, the CounterApp component is using connect without using any of the props.
Try this instead:
Reducer:
import * as types from '../actions/actionTypes'
const initialState = {
messages: []
}
export default function messages(state = initialState, action) {
switch(action.type) {
case types.MESSAGE_POST_DONE:
return {
...state,
messages: action.messages
}
default:
return state;
}
}
CounterApp:
class CounterApp extends Component {
render() {
return (
<Messaging />
);
}
}
Messaging:
class Messaging extends Component {
render() {
return (
<View>
<MessageList messages={this.props.messages} />
<Message />
</View>
)
}
}
export default connect(state => ({
messages: state.messages.messages
}))(Messaging);