My react redux (4.4.5) project uses react-router-redux(4.0.5) and redux-async-connect (0.1.13). Before I load my container component, I want to asynchronously load data from my API. The url contains a query parameter named "category" which is used to fetch the messages. ie. user/cornel/messages?category=react-redux
The parameters linked to my location/path are in state.routing.locationBeforeTransitions, but these are not up to date when in the async call. I can get the path parameters from the params parameter that is passed to the async function, but this does not contain the query parameters.
#statics({
reduxAsyncConnect(params, store) {
const { dispatch } = store;
return Promise.all([
dispatch(loadMessages(category)) <-- need the query parameter "category" here
]);
}
})
#connect(state => ({
messages: state.user.messages
}))
export default class HomeContainer extends Component {
static propTypes = {
dispatch: PropTypes.func
messages: PropTypes.array.isRequired
};
render() {
const { messages } = this.props;
return (
...
}
}
}
Anyone has any idea how I should access the query parameter so it works both client and server side?
Thanks in advance!
You should be able to get search from redux state as the following if you are using react-redux-router.
#statics({
reduxAsyncConnect(params, store) {
const { dispatch } = store;
return Promise.all([
dispatch(loadMessages(category)) <-- need the query parameter "category" here
/* you might get
store.getState().
routing.locationBeforeTransitions.search
from here too */
]);
}
})
#connect(state => ({
messages: state.user.messages,
/* get search from redux state */
search : state.routing.locationBeforeTransitions.search
}))
export default class HomeContainer extends Component {
static propTypes = {
dispatch: PropTypes.func
messages: PropTypes.array.isRequired
};
render() {
const { messages } = this.props;
return (
...
}
}
}
Let me know if it is not available for you.
EDIT
Here is a piece of code that doesn't use reduxAsyncConnect and accomplishing what you want to do.
// CONSTANTS
const
GET_SOMETHING_FROM_SERVER = 'GET_SOMETHING_FROM_SERVER',
GET_SOMETHING_FROM_SERVER_SUCCESS = 'GET_SOMETHING_FROM_SERVER_SUCCESS',
GET_SOMETHING_FROM_SERVER_FAIL = 'GET_SOMETHING_FROM_SERVER_FAIL';
// REDUCER
const initialState = {
something : [],
loadingGetSomething: false,
loadedGetSomething:false,
loadGetSomethingError:false
};
export default function reducer(state = initialState, action) {
switch(action.type) {
case GET_SOMETHING_FROM_SERVER:
return Object.assign({}, state, {
loadingGetSomething: true,
loadedGetSomething:false,
loadGetSomethingError:false
something : [] // optional if you want to get rid of old data
});
case GET_SOMETHING_FROM_SERVER_SUCCESS:
return Object.assign({}, state, {
loadingGetSomething: false,
loadedGetSomething:true,
something : action.data
});
case GET_SOMETHING_FROM_SERVER_FAIL:
return Object.assign({}, state, {
loadingGetSomething: false,
loadGetSomethingError: action.data
});
default:
return state;
}
};
// ACTIONS
/* ----------------- GET SOMETHING ACTIONS START ----------------- */
import Fetcher from 'isomorphic-fetch'; // superagent , axios libs are okay also
export function getSomething() {
return {
type : GET_SOMETHING_FROM_SERVER
}
};
export function getSomethingSuccess(data) {
return {
type : GET_SOMETHING_FROM_SERVER_SUCCESS,
data
}
};
export function getSomethingFail(data) {
return {
type : GET_SOMETHING_FROM_SERVER_FAIL,
data
}
};
export function getSomethingAsync(paramsToBeSentFromComponents){
return function(dispatch) {
const fetcher = new Fetcher();
dispatch(getSomething()); // so we can show a loading gif
fetcher
.fetch('/api/views', {
method : 'POST',
data : {
// use paramsToBeSentFromClient
}
})
.then((response) => {
dispatch( getSomethingSuccess(response.data));
})
.catch((error) => {
return dispatch(getSomethingFail({
error : error
}))
});
}
}
/* ----------------- GET SOMETHING ACTIONS END ----------------- */
// COMPONENT
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as somethignActions from './redux/something';
#connect((state) => ({
pathname : state.routing.locationBeforeTransitions.pathname,
something : state.something
}))
export default class SettingsMain extends Component{
constructor(props){
super(props);
this.somethingActions = bindActionCreators(somethingActions, this.props.dispatch);
}
componentDidMount(){
// you are free to call your async function whenever
this.settingActions.getSomething({ this.props.pathname...... })
}
render(){
return ( /* your components */ )
}
}
Related
I have write following function in react js view recordings.js
fetchRecordingJson(file_name)
{
const { dispatch, history } = this.props;
if(dispatch)
{
dispatch(fetchrecordingJson(file_name));
history.push('/app/recording-analysis');
}
}
I want to travel data coming from using function fetchrecordingJson(file_name) to another page recording-analysis
Query Is:
How do I see the data coming from function fetchrecordingJson(file_name) in recording-analysis page
I am using redux-thunk library for async calls and below is my reducer code
case 'RECEIVE_JSON':
let newState = {data: action.data.data, info: action.data.info, count: state.count};
newState.annotations = action.data.annotations.length === 0 ? [[]] : action.data.annotations || [[]];
newState.file_name = action.file_name;
return Object.assign({}, newState);
below is my action.js code
export function receiveJSON(json, file_name) {
return {
type: RECEIVE_JSON,
file_name,
data: json
}
}
export function fetchRecordingJson(file_name) {
return dispatch => {
return axios.get(API_URL+`fetchjson/${file_name}`)
.then(json => {
dispatch(receiveJSON(json.data, file_name))
})
}
}
You can get the data saved in the redux store by using the mapStateToProps property of redux. I have created a sample component in which we are getting the newState value that you have just stored in the redux store in componentDidMount by calling this.props.data.
import React, { Component } from 'react'
import { connect } from 'react-redux'
class TestOne extends Component {
componentDidMount = () => {
console.log(this.props.data)
}
render() {
return (
<>
</>
)
}
}
const mapStateToProps = state => {
return {
data: state.newState
}
}
export default connect( mapStateToProps )(TestOne)
I am trying to get the details of film using redux into the react component
but getting it like undefined , here is my code :
Film Service :
import axios from 'axios';
import config from '../config/config';
export const FilmServices = {
getAllFilms,
searchFilm,
getFilmByID
};
function getFilmByID(apiEndpoint){
return axios.get(config.detailsUrl+apiEndpoint).then((response)=>{
return response;
}).catch((err)=>{
console.log(err);
})
}
Film Action :
import {FilmServices} from '../../services/FilmServices'
export function getFilmByID(idFilm) {
return dispatch => {
FilmServices.getFilmByID(idFilm)
.then((response) => {
if (response) {
dispatch(GET_FILMS_BY_ID(response));
}
})
}
}
function GET_FILMS_BY_ID(response){
return{
type: "GET_FILMS_BY_ID",
payload: response
}
}
Film Reducer :
const initialState = { film: {}}
export function filmreducer(state = initialState, action) {
case 'GET_FILMS_BY_ID':
console.log(action.payload.data)
return{
...state,
film : action.payload.data
};
default:
return state
}
}
And Details Component
import React, {Component} from 'react';
import connect from "react-redux/es/connect/connect";
import {getFilmByID} from "../store/actions/FilmActions";
class Details extends Component {
componentDidMount() {
const {idFilm} = this.props.match.params
const {dispatch } = this.props;
dispatch(getFilmByID(idFilm));
}
constructor(props) {
super(props);
}
render() {
const {film} = this.props.Film;
return (
<React.Fragment>
{/*<h1>{this.idFilm}</h1>*/}
<h1>{film.name}</h1>
</React.Fragment>
);
}
}
const mapStateToProps = (state) =>{
const { Film } = state.filmreducer;
return {
Film
};
}
export default connect(
mapStateToProps
)(Details);
I am getting this error :
TypeError: Cannot read property 'film' of undefined
But when i log my data from filmAction i'm correctly getting the response but not in the component .
Can any one help me please ?
Looks like it's just a situation of cases.
find fixes below
...
render() {
const { film } = this.props;
...
}
}
const mapStateToProps = (state) =>{
const { film } = state.filmreducer;
return {
film
};
}
I'm using redux with redux-observable and get this strange error:
Actions must be plain objects. Use custom middleware for async >actions.
/* Component.jsx */
import React from "react"
import { serialNumberCheck } from '../actions'
const Component = props => {
...
<button
onClick={() => props.serialNumberCheck('123456789123456')}
>
Check
</button>
...
}
const mapDispatchToProps = dispatch =>
bindActionCreators({serialNumberCheck}, dispatch)
export default compose(
reduxForm({
...
}),
withStyles(styles),
connect(mapDispatchToProps)
)(Component)
/* actions.js */
export const SERIAL_NUMBER_CHECK = 'SERIAL_NUMBER_CHECK'
export const SERIAL_NUMBER_CHECK_SUCCESS = 'SERIAL_NUMBER_CHECK_SUCCESS'
export const serialNumberCheck = (serialNumber) => ({
type: SERIAL_NUMBER_CHECK,
payload: serialNumber
})
export const serialNumberCheckSuccess = (data) => ({
type: SERIAL_NUMBER_CHECK,
payload: data
})
/* epics.js */
...
import { serialNumberCheck } from "../actions"
import ... from 'rxjs'
...
function serialNumberCheckEpic(action$) {
return action$
.ofType(SERIAL_NUMBER_CHECK)
.switchMap((data) => {
return ajax.getJSON(`http://localhost:3004/sn/?sn=${data.payload}`)
.map((data) => data)
})
.map(data => {
if(data.length !== 0) {
serialNumberCheckSuccess({success: true});
}
})
}
...
export const rootEpic = combineEpics(
...
serialNumberCheckEpic
);
/* reducer.js */
import {
SERIAL_NUMBER_CHECK_SUCCESS,
} from '../actions'
...
export default function epicReducer(state = initialState, action) {
switch (action.type) {
case SERIAL_NUMBER_CHECK_SUCCESS:
return {
...state,
success: action.payload
}
}
}
/* JSON-SERVER RESPONSE */
[
{
"id": 1,
"sn": "123456789123456"
}
]
Inside component i'am calling function serialNumberCheck() and passing inside sierial number that we need to check.
Inside Epic im passing serial number to json-server that checks if this number exists in my "database". If serial number exists, server response is .json containing some parameters.
So if response isn't empty we need to write success: true inside redux store.
But in the end we get successfull GET request, and then error: Actions must be plain objects. Use custom middleware for async actions., but no changes inside redux-store and nothing from SERIAL_NUMBER_CHECK_SUCCESS action.
Finally, I found the solution. I've just missed the return before calling action inside my epic.
function serialNumberCheckEpic(action$) {
return action$
.ofType(SERIAL_NUMBER_CHECK)
.switchMap((data) => {
return ajax.getJSON(`http://localhost:3004/sn/?sn=${data.payload}`)
.map((data) => data)
})
.map(data => {
if(data.length !== 0) {
+ return serialNumberCheckSuccess({success: true});
}
})
}
I am trying to get a simple react-redux app to work and I am running into a weird error that I can't figure out. I am trying to simply set my current user's first name and handle the store and one set function works and the other doesn't.
setCurrentUserFirstName - works
setCurrentUserHandle - doesn't
import React, { Component } from 'react';
import { Link } from 'react-router';
import { connect } from 'react-redux';
import store from '../../store';
var Utilities = require('../../../common/commonutilities.js');
var RestClient = require('../../../common/restClient.js');
//actions
import { setCurrentUserHandle, setCurrentUserFirstName } from '../../actions/userActions';
class Header extends Component {
constructor(props) {
super(props);
this.state = {};
RestClient.api.fetchGet('/getcurrentuser', (response) => {
if(response.success) {
this.setState({
isAuthenticated: true,
currentUser: response.currentUser
});
store.dispatch({
type: 'USER_DID_LOGIN',
userLoggedIn: true
});
//works fine
this.props.setCurrentUserFirstName(response.currentUser.firstName);
//doesn't work and throws the error: "TypeError: _this.props.setCurrentUserHandle is not a function"
this.props.setCurrentUserHandle(response.currentUser.handle);
}
},
(err) => {
console.log(err);
});
}
render() {
return (
{this.props.user.currentUserFirstName}, {this.props.user.currentUserHandle}
);
}
}
const mapStateToProps = function(store) {
return {
//user properties
user: store.userState
};
};
const mapDispatchToProps = (dispatch) => {
return{
setCurrentUserFirstName: (currentUserFirstName) =>{
dispatch( setCurrentUserFirstName(currentUserFirstName));
}
}
return{
setCurrentUserHandle: (currentUserHandle) =>{
dispatch( setCurrentUserHandle(currentUserHandle));
}
}
};
//connect it all
export default connect(mapStateToProps, mapDispatchToProps)(Header);
I have them as actions in the userActions.js file
export function setCurrentUserFirstName(currentUserFirstName){
return{
type: 'SET_CURRENT_USER_FIRST_NAME',
payload: currentUserFirstName
};
}
export function setCurrentUserHandle(currentUserHandle){
return{
type: 'SET_CURRENT_USER_HANDLE',
payload: currentUserHandle
};
}
And in the reducer
const initialUserState = {
user: {},
currentUserFirstName:[],
currentUserHandle:[]
};
// The User reducer
const userReducer = (state = initialUserState, action) => {
//using newState object to be immutable
let newState = state;
switch (action.type) {
case 'SET_CURRENT_USER_FIRST_NAME':
newState = {
...state,
currentUserFirstName: action.payload
};
break;
case 'SET_CURRENT_USER_HANDLE':
newState = {
...state,
currentUserHandle: action.payload
};
break;
break;
default:
break;
}
return newState;
};
export default userReducer;
What do I have incorrect?
You have 2 return statements in your mapDispatchToProps - the second one will never be reached. You can return a single object as follows:
const mapDispatchToProps = (dispatch) => {
return{
setCurrentUserFirstName: (currentUserFirstName) =>{
dispatch( setCurrentUserFirstName(currentUserFirstName));
},
setCurrentUserHandle: (currentUserHandle) =>{
dispatch( setCurrentUserHandle(currentUserHandle));
}
}
};
In addition to Tony's correct answer, I highly encourage that you use the "object shorthand" form of mapDispatch instead. You can pass an object full of action creators as the second argument to connect(), instead of an actual mapDispatch function. In your case, it'd look like:
import { setCurrentUserHandle, setCurrentUserFirstName } from '../../actions/userActions';
const actionCreators = { setCurrentUserHandle, setCurrentUserFirstName };
class Header extends Component {}
export default connect(mapStateToProps, actionCreators)(Header);
I am in the process of migrating an app from React to React Native and am running into an issue with Redux not dispatching the action to Reducer.
My root component looks like this:
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux';
import Main from '../main/main';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
class App extends Component {
render() {
console.log('Rendering root.js component');
console.log(this.props);
const { dispatch, isAuthenticated, errorMessage, game, communication } = this.props
return (
<View style={styles.appBody}>
<Main
dispatch={dispatch}
game={game}
communication={communication}
/>
</View>
)
}
}
App.propTypes = {
dispatch: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool.isRequired,
errorMessage: PropTypes.string,
}
function mapStateToProps(state) {
const { auth } = state
const { game } = state
const { communication } = state
const { isAuthenticated, errorMessage } = auth
return {
isAuthenticated,
errorMessage,
game,
communication
}
}
const styles = StyleSheet.create({
appBody: {
}
});
export default connect(mapStateToProps)(App)
Then a 'lobby' subcomponent has the dispatch function from Redux as a prop passed to it. This component connects to a seperate javascript file, and passes the props to it so that that seperate file has access to the dispatch function:
componentWillMount() {
coreClient.init(this);
}
In that file I do this:
const init = function(view) {
socket.on('connectToLobby', (data) => {
console.log('Lobby connected!');
console.log(data);
console.log(view.props) // shows the dispatch function just fine.
view.props.dispatch(connectLobbyAction(data));
});
}
The action itself also shows a console log I put there, just that it never dispatches.
export const LOBBY_CONNECT_SUCCESS = 'LOBBY_CONNECT_SUCCESS';
export function connectLobbyAction(data) {
console.log('Action on connected to lobby!')
return {
type: LOBBY_CONNECT_SUCCESS,
payload: data
}
}
I feel a bit lost, would appreciate some feedback :)
EDIT: Reducer snippet:
var Symbol = require('es6-symbol');
import {
LOBBY_CONNECT_SUCCESS
} from './../actions/actions'
function game(state = {
//the state, cut to keep things clear.
}, action) {
switch (action.type) {
case LOBBY_CONNECT_SUCCESS:
console.log('reducer connect lobby')
return Object.assign({}, state, {
...state,
user : {
...state.user,
id : action.payload.id,
connected : action.payload.connected
},
match : {
...state.match,
queuePosition : action.payload.position,
players : action.payload.playerList,
room : 'lobby'
},
isFetching: false,
})
default:
return state
}
}
const app = combineReducers({
game,
//etc.
})