I am trying to use onClick to send the two different state information to action event.
The user.user.user_id and post.book_id is from application state and I want to pass that information to action.
<div
onClick={() => { this.props.addToMyPage({
user_id: user.user.user_id, book_id: post.book_id}) }}>
Add this to my page
</div>
The part
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { selectBook } from '../actions/index';
import { addToMyPage } from '../actions/index';
import { Link } from 'react-router';
class BookDetails extends Component {
componentWillMount() {
this.props.selectBook(this.props.params.id);
}
render() {
const {post} = this.props;
const {user} = this.props;
console.log("This Book", post)
if(!post) {
return <div>Loading...</div>
}
return (
<div>
<h1>Title: {post.title}</h1>
<h2>Pages: {post.pages}</h2>
<div>Reviews:</div>
<div
onClick={() => { this.props.addToMyPage(user.user, post) }}>
Add this to my page
</div>
</div>
)
}
}
function mapStateToProps(state) {
return {
post: state.books.post,
user: state.user.post
}
}
export default connect(mapStateToProps, {selectBook, addToMyPage})(BookDetails);
When I do this it only passes the user.user information and in the google console it says
Actions must be plain objects. Use custom middleware for async actions.
Not exactly sure what this means.
I have set up middleware in my index.js file.
//index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { Router, browserHistory } from 'react-router';
import reducers from './reducers';
import routes from './routes';
import promise from 'redux-promise';
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<Router history={browserHistory} routes={routes} />
</Provider>
, document.querySelector('.container'));
//action
export function addToMyPage(user, post) {
console.log("mypage" , user, post);
const request = axios.post(`${ROOT_URL}api/mypage`, user, post);
return {
type: ADD_MYPAGE,
payload: request
}
}
Answered by Rob M need to have object return in your action file. I commented out the return object which threw the error.
function addToMyPage(user, post) { return { type: 'ADD_TO_MY_PAGE', user, post }; }
Related
So, when I am trying to access my news props to display news title to my page I am receiving and error like this one:
Error Msg
import React, { Component } from 'react';
import Search from './Search';
import { connect } from 'react-redux';
import NewsItem from './NewsItem';
class NewsResults extends Component {
render() {
return (
<div>
<Search />
{this.props.news.map(item => {
return <NewsItem new={item} key={item.objectID} showButton={true}/>
})
}
</div>
)
}
}
function mapStateToProps(state) {
console.log(state)
return {
news: state.news
}
}
export default connect(mapStateToProps, null)(NewsResults);
NewsItem.js is just a component through which I will display the results on my application.
import React, { Component } from 'react';
class NewsItem extends Component {
render(){
return(
<div className="col-sm-12 col-sm-3">
<div className="thumbnail">
<div className="caption">
<h3>{this.props.new.title}</h3>
</div>
</div>
</div>
)
}
}
export default NewsItem;
This is where I'm fetching my information from an api and then I want to display on my page using maps
class Search extends Component {
constructor(props) {
super(props);
this.state = {
query: ''
};
}
search(){
console.log('Search button clicked', this.state.query);
let url = `http://hn.algolia.com/api/v1/search_by_date?query=${this.state.query}`;
// console.log(url);
fetch(url, {
method: 'GET'
}).then(response=> response.json())
.then(jsonObject => {this.props.news(jsonObject.results)});
}
This is my redux action code in action.js file
export const NEWS = "NEWS";
export function news(items) {
const action = {
type: NEWS,
items
}
return action;
}
This is the reducer file
import { NEWS } from '../actions';
const initialState = {
news: []
}
export default function (state= initialState, action) {
switch(action.type) {
case NEWS:
console.log("News are ",action.items);
return {
...state,
news: action.items
};
default:
return state;
}
};
So now I have changed my reducer to be in 2 seperate files I will post below. Also by doing that error message changed to this:
This is the new Error I am getting now
My reducer files are below:
import { NEWS } from '../actions';
export default function news(state = [], action) {
switch(action.type) {
case NEWS:
console.log("News are ",action.items);
return action.items;
default:
return state;
}
}
Second Reducer File:
import news from './news_reducer';
import { combineReducers } from 'redux';
const rootReducer = combineReducers({
news
});
export default rootReducer;
Then I am importing my rootReducer from reducer folder in index.js outside of source folder where my redux store is.
This is how I am creating my store in index.js
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './components/App';
import registerServiceWorker from './registerServiceWorker';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './reducers';
const store = createStore(rootReducer,
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>
, document.getElementById('root'));
registerServiceWorker();
Now, My problem is that I am trying to send data from actions but reducer isn't recieving it for some reason.
Another Error popped up now. Another Error
We are currently struggling using Apollo Client to manage local state in a react application. We were able to build up a very simple example where we just display a div depending on the result of a mutation. The effect of the mutation should only be to display an alert message and return nothing.
We have come up with the following very simple example code:
File index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { ApolloClient } from 'apollo-client';
import { ApolloProvider } from 'react-apollo';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { withClientState } from 'apollo-link-state';
import App from './App';
const resolvers = {
Mutation: {
setData: (_, {myBool}) => {
alert(myBool);
return null;
}
}
};
const client = new ApolloClient({
cache: new InMemoryCache(),
link: withClientState({ resolvers })
});
ReactDOM.render(
<ApolloProvider client={client}>
<App />
</ApolloProvider>,
document.getElementById('root')
);
File App.js:
import React, { Component, Fragment } from 'react';
import gql from 'graphql-tag';
import { Mutation } from 'react-apollo';
class App extends Component {
setData = () => {
alert('setData is called');
const SET_DATA = gql`
mutation SetData($myBool: Boolean!) {
setData(myBool: $myBool) #client
}
`;
return (
<Mutation mutation={SET_DATA} variables={{ myBool: true }}>
{(_, { loading, error }) => (
<div>
{loading && <p>...loading...</p>}
{error && <p>ERROR ! Try reloading.</p>}
</div>
)}
</Mutation>
);
};
render() {
return (
<Fragment>
<h1>App</h1>
{this.setData()}
</Fragment>
);
}
}
export default App;
Could someone please tell us why the alert method we call in the setData() resolver (from file index.js) isn't called upon page loading?
I have reactjs setup with routes but my routing is not working. When I load the page it works but when I click on the links the URL changes but the component does not render. I tried to put as much as I can in the sandbox. load with URL/admin and click on logout etc.
https://codesandbox.io/s/o5430k7p4z
index
import React, { Component } from 'react'
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, combineReducers } from 'redux';
import { BrowserRouter, Route, browserHistory } from 'react-router-dom';
import promise from 'redux-promise';
import { createLogger } from 'redux-logger';
import App from './App'
import reducers from './reducers';
require("babel-core/register");
require("babel-polyfill");
import 'react-quill/dist/quill.snow.css'; // ES6
const logger = createLogger();
const initialState = {};
const createStoreWithMiddleware = applyMiddleware(promise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>
, document.getElementById('root'));
App
import React, { Component } from 'react'
import { Switch, Route } from 'react-router-dom';
import ReactGA from 'react-ga';
ReactGA.initialize('UA-101927425-1');
import { connect } from 'react-redux';
import { fetchActiveUser } from './actions/index';
import { bindActionCreators } from 'redux';
import {getHttpRequestJSON} from './components/HTTP.js'
import Header from './components/header';
import Logout from './components/logout';
import SideBar from './components/sidebar';
import HomeContent from './containers/home';
import Ldapuser from './components/ldapuser';
import Admin from './components/admin/admin';
function fireTracking() {
ReactGA.pageview(window.location.pathname + window.location.search);
}
class App extends Component {
constructor(props){
super(props);
this.state = {
isGuest : false,
isSupp : false,
loading: true,
version: '',
};
}
initData = () => {
let self = this;
getHttpRequestJSON('/api/user/get/user/method/is/guest/format/json?quiet=1')
.then((response) => {
let isGuest = response.body.recordset.record.isGuest;
if(isGuest){
/*$(".logo").trigger('click');
//$("#overlay").show();
$('#modalIntro').modal('toggle');
$("#modalIntro").on("hidden.bs.modal", function () {
$(".logo").trigger('click');
});*/
}
self.props.isGuest = isGuest;
self.props.loading = false;
//self.props.version = response.header.version;
self.setState({
loading : false,
version : response.header.version,
isGuest : isGuest
});
})
.catch(error => {
console.log("Failed!", error);
//$('#myModalError .modal-body').html(error);
//$('#myModalError').modal('show');
});
getHttpRequestJSON('/api/user/get/user/method/is/supp/format/json?quiet=1')
.then((response) => {
self.setState({
isSupp : response.body.recordset.record.isSupp
});
})
.catch(error => {
console.log("Failed!", error);
//$('#myModalError .modal-body').html(error);
//$('#myModalError').modal('show');
});
}
componentDidMount() {
this.props.fetchActiveUser();
this.initData();
}
render() {
return (
<div>
<Header activeUser={this.props.activeUser} loading={this.state.loading} version={this.state.version} title={`Home`} />
<SideBar />
<main>
<Switch>
<Route path='/index.html' render={()=><HomeContent activeUser={this.props.activeUser} isGuest={this.state.isGuest} isSupp={this.state.isSupp} />} />
<Route path='/home' render={()=><HomeContent activeUser={this.props.activeUser} isGuest={this.state.isGuest} isSupp={this.state.isSupp} />} />
<Route path='/logout' component={Logout}/>
<Route path='/ldapuser' component={Ldapuser}/>
<Route path='/admin' render={()=><Admin isGuest={this.state.isGuest} isSupp={this.state.isSupp}/>} />
</Switch>
</main>
</div>
);
}
}
//export default App;
function mapStateToProps(state) {
if(state.activeUser.id > 0){
ReactGA.set({ userId: state.activeUser.id });
}
// Whatever is returned will show up as props
// inside of the component
return {
activeUser: state.activeUser
};
}
// Anything returned from this function will end up as props
// on this container
function mapDispatchToProps(dispatch){
// Whenever getUser is called, the result should be passed
// to all our reducers
return bindActionCreators({ fetchActiveUser }, dispatch);
}
//Promote component to a container - it needs to know
//about this new dispatch method, fetchActiveUser. Make it available
//as a prop
export default connect(mapStateToProps, mapDispatchToProps)(App);
The codesandbox is not working, but I think what is happening to you is a very common problem when using react-redux and react-router. The connect HOC of react-redux has a builtin SCU (shouldComponentUpdate), so for it to know to rerender is requires to receive new props. This can be done using the withRouter hoc of react-router. Simply wrap connect(..)(MyComponent) with withRouter(connect(..)(MyComponent)) or do it clean and use compose (from recomponse for example);
const enhance = compose(
withRouter,
connect(mapStateToProps)
)
export default enhance(MyComponent)
Make sure not to do it the other way around, because that does not work.
I am working on a Blog project in redux where I am calling data from an api server and trying to display the default data(for now I am trying to retrieve the default data,I haven't implemented posting to the api server as of now) from the server.The data contains posts that are posted by users on the blog.The default data for the api server looks like this:
const defaultData = {
"8xf0y6ziyjabvozdd253nd": {
id: '8xf0y6ziyjabvozdd253nd',
timestamp: 1467166872634,
title: 'Udacity is the best place to learn React',
body: 'Everyone says so after all.',
author: 'thingtwo',
category: 'react',
voteScore: 6,
deleted: false,
commentCount: 2
},
"6ni6ok3ym7mf1p33lnez": {
id: '6ni6ok3ym7mf1p33lnez',
timestamp: 1468479767190,
title: 'Learn Redux in 10 minutes!',
body: 'Just kidding. It takes more than 10 minutes to learn technology.',
author: 'thingone',
category: 'redux',
voteScore: -5,
deleted: false,
commentCount: 0
}
}
So,what I am trying to do is, I want to filter out the posts based on a category.So,I have the homepage which lists all the categories available.So,when the user clicks on a category,he/she will be taken to a page where the posts for that category are shown.
The redux's "Action" file for filtering out the posts according to a particular category is given below:
import axios from 'axios';
export const FETCH_CATEGORIES = 'fetch_categories';
export const FETCH_PARTICULAR_CATEGORY_POSTS = 'fetch_particular_category';
let token;
if (!token)
token = localStorage.token = Math.random().toString(32).substr(-8);
const API = 'http://localhost:3001';
const headers = {
'Accept' : 'application/json',
'Authorization' : 'token'
}
//Action creaor for fetching all the categories available
export function fetchCategories() {
const URL = `${API}/categories`;
const request = axios.get(URL,{headers});
return dispatch => {
return request.then((data) => {
dispatch({
type : FETCH_CATEGORIES,
payload : data
})
})
}
}
//Action creator to fetch all the available posts for a particular category
export function fetchPostWithCateogry(category) {
const URL = `${API}/${category}/posts`;
const request = axios.get(URL,{headers});
return dispatch => {
return request.then((data) => {
console.log(data);
dispatch({
type: FETCH_PARTICULAR_CATEGORY_POSTS,
payload: data
})
})
}
}
The component file for displaying the posts of a particular category is:
import React, { Component } from 'react';
import { fetchPostWithCateogry } from '../actions/categories_action';
import { connect } from 'react-redux';
class CategoryView extends Component {
componentDidMount() {
const { category } = this.props.match.params;
this.props.fetchPostWithCateogry(category);
console.log(category);
}
render() {
const { category } = this.props;
console.log(category);
if (!category) {
return <div>Loading...</div>
}
return(
<div>
<h3>category.title</h3>
<h5>category.category</h5>
<h6>category.body</h6>
</div>
);
}
}
function mapStateToProps({ categories },ownProps) {
return { category: categories[ownProps.match.params.category]}
}
export default connect(mapStateToProps, {fetchPostWithCateogry})(CategoryView);
The reducer file for the same is:
import _ from 'lodash';
import { FETCH_CATEGORIES, FETCH_PARTICULAR_CATEGORY_POSTS } from '../actions/categories_action';
export default function(state={}, action) {
switch(action.type) {
case FETCH_CATEGORIES:
return {categories: {...state.categories, ...action.payload.data.categories}};
//return {categories: [...state.categories]};
case FETCH_PARTICULAR_CATEGORY_POSTS:
console.log(action.payload);
return {...state, [action.payload]: action.payload};
default:
return state;
}
}
I am using Route to navigate to the component depending on the endpoint the user entered.The index file for the same is given below:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { BrowserRouter, Route } from 'react-router-dom';
import thunk from 'redux-thunk';
import './index.css';
import App from './App';
import reducers from './reducers/index.js'
import Posts from './components/posts_index';
import CreatePost from './components/new_post';
import PostDetail from './components/post_detail';
import CategoryView from './components/category';
const createStoreWithMiddleware = createStore(reducers,applyMiddleware(thunk));
ReactDOM.render(
<Provider store={createStoreWithMiddleware}>
<BrowserRouter>
<div>
<Route path="/new" component={CreatePost} />
<Route path="/posts/:id" component={PostDetail} />
<Route exact path="/" component={Posts} />
<Route path="/:category/posts" component={CategoryView} />
</div>
</BrowserRouter>
</Provider> , document.getElementById('root'));
So,I am talking about the last route above.Now,the problem is, I do not get any errors,but the posts for a category are not being displayed.But if I try to console.log the request response returned from axios,I can see the required result,i.e.,I see the post with the category which is enter in my route url.A screenshot of the output of console.log that I get is attached below:
Can anyone please suggest where am I going wrong? Thanks in advance.
EDIT 1:
After trying to use redux devtools,I get the below error as shown in the screenshot:
EDIT 2
the output of promise:
NOTE I get the error only when I try to use redux devtools.Am I doing something wrong while using it.Below is my index file:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import { BrowserRouter, Route } from 'react-router-dom';
import thunk from 'redux-thunk';
import './index.css';
import App from './App';
import reducers from './reducers/index.js'
import Posts from './components/posts_index';
import CreatePost from './components/new_post';
import PostDetail from './components/post_detail';
import CategoryView from './components/category';
const createStoreWithMiddleware = createStore(reducers,applyMiddleware(thunk),window.__REDUX_DEVTOOLS_EXTENSION__ &&
window.__REDUX_DEVTOOLS_EXTENSION__());
ReactDOM.render(
<Provider store={createStoreWithMiddleware}>
<BrowserRouter>
<div>
<Route path="/new" component={CreatePost} />
<Route path="/posts/:id" component={PostDetail} />
<Route exact path="/" component={Posts} />
<Route path="/:category/posts" component={CategoryView} />
</div>
</BrowserRouter>
</Provider> , document.getElementById('root'));
Edit 3:
Actions in redux devtools:
Edit 4
redux devtools State screenshot
Edit : I edited all my post to make it clear.
Your reducer :
import _ from 'lodash';
import { FETCH_CATEGORIES, FETCH_PARTICULAR_CATEGORY_POSTS } from '../actions/categories_action';
export default function(state={}, action) {
switch(action.type) {
case FETCH_CATEGORIES:
return {categories: {...state.categories, ...action.payload.data.categories}};
case FETCH_PARTICULAR_CATEGORY_POSTS:
return {...state, [action.category]: action.payload.data};
default:
return state;
}
}
Your action :
export function fetchPostWithCateogry(category) {
const URL = `${API}/${category}/posts`;
const request = axios.get(URL,{headers});
return dispatch => {
return request.then((data) => {
dispatch({
type: FETCH_PARTICULAR_CATEGORY_POSTS,
payload: data,
category
})
})
}
}
Now we pass the category property to the action to name the index in which we'll store our array of posts.
You'll access this array of posts in your component but you need to iterate in this array to display his content :
import React, { Component } from 'react';
import { fetchPostWithCateogry } from '../actions/categories_action';
import { connect } from 'react-redux';
class CategoryView extends Component {
componentDidMount() {
const { category } = this.props.match.params;
this.props.fetchPostWithCateogry(category);
console.log(category);
}
render() {
const { category } = this.props;
console.log(category);
if (!category) {
return <div>Loading...</div>
}
return(
<div> {category.map(post => (<h3>{post.title}</h3>))}
</div>
);
}
}
function mapStateToProps({ categories },ownProps) {
return { category: categories[ownProps.match.params.category]}
}
export default connect(mapStateToProps, {fetchPostWithCateogry})(CategoryView);
I cant figure out what is going on. I have redux-thunk setup just like always. For some reason that I can not figure out I get the error: Error: Actions must be plain objects. Use custom middleware for async actions. can anyone help me figure this error out?
Index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './app';
import './index.css';
ReactDOM.render(
<App />,
document.getElementById('root')
);
CreateStore.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from './reducers';
export default function configureStore(initialState) {
return createStore(
reducers,
initialState,
applyMiddleware(thunk)
);
}
app.js
import React, { Component } from 'react';
import Routes from './Routes';
import {Provider} from 'react-redux';
import configureStore from './configureStore';
const store = configureStore();
class App extends Component {
render(){
return (
<Provider store={store}>
<Routes/>
</Provider>
);
}
}
export default App;
Today.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { autoLocate } from '../actions';
import { Button } from './Common'
class Today extends Component {
componentDidMount() {
this.props.fetchTodayWeather('http://
api.wunderground.com/api/cc9e7fcca25e2
ded/geolookup/q/autoip.json');
}
render() {
return (
<div>
<div style={styles.Layout}>
<div><Button> HAHAH </Button></div>
<div><Button> Weather Now </Button></div>
</div>
</div>
);
}
}
const mapStateToProps = state => {
const loading = state.locate.loading;
const located = state.locate.autolocation;
return{ loading, located };
};
const mapDispatchToProps = (dispatch) => {
return {
fetchTodayWeather:(Url) => dispatch(autoLocate(Url))
};
};
export default connect(mapStateToProps,mapDispatchToProps)(Today);`
autoLocate.js
import { AUTODATA,
AUTOLOCATING
} from './types';
export const autoLocate = (url) => {
return (dispatch) => {
dispatch({ type: AUTOLOCATING });
fetch(url)
.then(data => fetchSuccess(dispatch, data))
};
};
const fetchSuccess = (dispatch, data) => {
dispatch({
type: AUTODATA,
payload: data
});
};