Iam new to redux/react . Iam a doing a simple rest call to fetch some details by triggering an action . But iam getting the following error
BSC.js?d219:24 Uncaught TypeError:
this.props.fb is not a function.
My code is as follows
Actions-
export function fb(){
return dispatch => {
dispatch({
type: SUCCESS,
payload: {"key1":"value1","key2":"value2"}
})
}
}
Container -
import {connect} from 'react-redux';
import React, { Component } from 'react';
import {fb} from '../../actions/b/bSA';
export class BSC extends React.Component {
componentDidMount(){
this.props.fb();
}
render(){
return <div></div>
}
}
export default connect(null, {fB})(BSC);
Reducer
const initialState = {
loading: true,
error: null,
};
export default function bSR(state = initialState, action) {
switch(action.type) {
case SUCCESS:
return {
...state,
loading: false,
};
default:
// ALWAYS have a default case in a reducer
return state;
}
}
What could be the issue ?
Just spit balling, but I believe you are lacking a MapStateToProps function which would actually attach that function.
You need to declare this method in your container component file.
const mapDispatchToProps = dispatch => {
return bindActionCreators({
fb
}, dispatch);
};
where bindActionCreators is:
import {bindActionCreators} from "redux";
Now pass it to connect method:
export default connect(null, mapDispatchToProps)(BSC);
What happens here is mapDispatchToProps function gets dispatch as function parameter and it has to be passed to action creator to actually dispatch the action. Here bindActionCreators calls your action creator method with the dispatch as parameter and you will be able to dispatch the required action there.
Related
I'm trying pass the data from reducer to component and receive as props.
But the data return UNDEFİNED, so I have tried console the data on reducer and action, but it's okey. There isn't any problem with the data coming from the API, but it always return to component undefined. Where is my fault?
Action
export default ProfileTab;
import axios from 'axios';
import { BASE, API_KEY } from '../config/env';
export const FETCHED_MOVIES = 'FETCHED_MOVIES';
export function fetchMovies() {
return (dispatch) => {
axios
.get(`${BASE}s=pokemon&apikey=${API_KEY}`)
.then((result) => result.data)
.then((data) =>
dispatch({
type: FETCHED_MOVIES,
payload: data.Search,
}),
);
};
}
Reducer
import { FETCHED_MOVIES } from '../actions/movies';
const initialState = {
fetching: false,
fetched: false,
movies: [],
error: {},
};
export default (state = initialState, action) => {
switch (action.type) {
case 'FETCHED_MOVIES':
return {
...state,
movies: action.payload,
};
default:
return state;
}
};
Component
import React, { Component } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { fetchMovies } from '../../actions/movies';
class Case extends Component {
static propTypes = {
movies: PropTypes.object.isRequired,
};
constructor(props) {
super(props);
}
componentDidMount() {
this.props.fetchMovies();
}
onChangeHandler = (e) => {
this.setState({
input: e.target.value,
});
};
render() {
console.log(this.props.movies);
return (
<div>
<div className="movies-root">
<div className="movies-wrapper">
<div className="movies-container safe-area">
<h1>mert</h1>
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
movies: state.movies,
};
};
const mapDispatchToProps = {
fetchMovies,
};
export default connect(mapStateToProps, mapDispatchToProps)(Case);
Do this in the connect statement:
export default connect(mapStateToProps,{fetchMovies})(Case);
And remove the mapDispatchToProps function from your code.
Dispatching props as an object is quite incorrect. Try this, and it should work.
That's because your mapDispatchToProps function should return an object and take dispatch as parameter. Each field in your returned object should contain a function that dispatches your action.
So try something like this:
const mapDispatchToProps = dispatch => {
return {
fetchMovies: () => dispatch(fetchMovies())
}
}
Although there's already an accepted answer, I'm not sure how correct it is, as it's completely valid to pass mapDispatchToProps the way you did with the latest react (16.13.1) and react-redux (7.2.1) versions (I'm not sure about earlier versions).
Now, assuming your question contains the whole code, there are two important things missing:
Creating the store:
import { createStore } from "redux";
const store = createStore(reducer);
and passing it to the Provider component:
<Provider store={store}>
If you go ahead and do as above, you'll see that this.props.fetchMovies emits the following error:
Actions must be plain objects. Use custom middleware for async actions.
To fix it, do as it says and add a middleware, e.g. thunk:
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";
const store = createStore(rootReducer, applyMiddleware(thunk));
What follows is the full code. Note that I "split" fetchMovies into two functions: sync and async, for illustrating the difference usage between the two. I also modified your code (made is shorter, mostly) for this answer's readability. You can also see a live demo here:
File app.js
import React, { Component } from "react";
import { connect } from "react-redux";
import { fetchMoviesSync, fetchMoviesAsyncMock } from "./api";
class App extends Component {
componentDidMount() {
this.props.fetchMoviesSync();
this.props.fetchMoviesAsyncMock();
}
render() {
return (
<div>
<div className="movies-root">
<div className="movies-wrapper">
<div className="movies-container safe-area">
{this.props.movies.join("\n")}
</div>
</div>
</div>
</div>
);
}
}
const mapStateToProps = (state) => ({ movies: state.movies });
const mapDispatchToProps = {
fetchMoviesSync,
fetchMoviesAsyncMock
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
File api.js
export const FETCHED_MOVIES = "FETCHED_MOVIES";
export const fetchMoviesSync = () => ({
type: FETCHED_MOVIES,
payload: ["movie1", "movie2", "movie3", "movie4"]
});
export const fetchMoviesAsyncMock = () => (dispatch) => {
dispatch({
type: FETCHED_MOVIES,
payload: ["movie5", "movie6", "movie7", "movie8"]
});
};
File reducer.js
const initialState = {
movies: [],
};
export default (state = initialState, action) => {
switch (action.type) {
case "FETCHED_MOVIES":
return {
...state,
movies: state.movies.concat(action.payload)
};
default:
return state;
}
};
File index.js
import React from "react";
import ReactDOM from "react-dom";
import Case from "./app";
import reducer from "./reducer";
import { createStore, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import thunk from "redux-thunk";
let store = createStore(reducer, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<Case />
</Provider>,
document.getElementById("container")
);
File index.html
<body>
<div id="container"></div>
</body>
I am new to this React Redux thing.
I was trying to replicate the same tutorial from Traversy Media Youtube channel
Github: https://github.com/bradtraversy/lead_manager_react_django
and encounter some problem with this.props.function
I have tried reading around the Stackoverflow but I was not using mapDispatchToProps and I don't really understand it as I am trying to understand the tutorial
Music.js
export class Music extends Component {
static propTypes = {
musics: PropTypes.array.isRequired,
getMusics: PropTypes.func.isRequired
};
componentDidMount() {
console.log(this.props)
this.props.getMusics();
};}
const mapStateToProps = state => ({
musics: state.musics.musics
});
export default connect(
mapStateToProps, {getMusics}
)(Music);
I stripped the rendering part because I haven't implemented
types.js
export const GET_MUSICS = "GET_MUSICS"
actions.js
import axios from 'axios';
import {GET_MUSICS} from './types';
export const getMusics = () => dispatch => {
axios.get('/api/musics/').then(res => {
dispatch({type: GET_MUSICS, payload: res.data});
}).catch(err=> console.log(err));
}
reducer musics.js
import {GET_MUSICS} from '../actions/types.js';
const initialState = {
musics: []
}
export default function (state = initialState, action) {
switch (action.type) {
case GET_MUSICS:
return {
... state,
musics: action.payload
};
default:
return state;
}
}
This will throw the error this.props.getMusics is not a function
console.log(props) return empty object
You are exporting Music both as named export and default export. Make sure to use the default export because that is connected to store. i.e do this,
import Music from "./Music"
and not this,
import {Music} from "./Music"
I think it will help u try this
import {bindActionCreators} from 'redux'
...
function mapDispatchToProps(state) {
return {
musics: state.musics.musics
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({fetchMusics}, dispatch)
}
export default connect(mapStateToProps, mapDispatchToProps)(Music)
pls check the parent component you might not be passing the function correctly,also more code is required showing the parent component
I have a react app that is pulling down some data, It turns a promise so I am using Thunk. However when I log this.props, the getShift action prints out as a function.
The log returns:
{getShifts: ƒ}getShifts: ƒ ()proto: Object
Action:
import settings from '../../aws-config.js';
import Amplify, { Auth, API } from 'aws-amplify';
export const GET_STAFF_SHIFTS = 'get_staff_shifts';
export const SHIFTS_LOAD_FAIL = 'shifts_load_fail';
export const getShifts = () => dispatch => {
console.log('Fetching list of shifts for user...');
const request = API.get("StaffAPI", "/shifts", {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
})
.then(response =>
dispatch({
type: 'GET_STAFF_SHIFTS',
payload: response
})
)
.catch(err =>
dispatch({type: 'SHIFTS_LOAD_FAIL'})
)
}
reducer:
import { getShifts, GET_STAFF_SHIFTS} from '../actions';
export default function(state = {}, action) {
switch(action.type){
case GET_STAFF_SHIFTS:
return Object.assign({}, state,{
start_time: action.payload
})
default:
return state;
}
}
Action:
import React, { Component } from 'react';
import Amplify, { Auth, API } from 'aws-amplify';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {getShifts} from '../actions/index';
import settings from '../../aws-config.js';
Amplify.configure(settings);
class StaffRota extends Component {
componentWillMount() {
this.props.getShifts();
}
renderPosts(){
console.log(this.props);
return (
<div></div>
);
}
}
function MapDispatchToProps(dispatch) {
return bindActionCreators({ getShifts }, dispatch);
}
export default connect(null, MapDispatchToProps)(StaffRota);
The action creator is supposed to be a function, which is expected. So console logging this.props.getShifts will give you a function.
There are two issues here:
first thing you are dispatching the wrong action type
dispatch({type: 'GET_STAFF_SHIFTS', ... }) instead of dispatch({type: GET_STAFF_SHIFTS, ... }) which you are expecting in your reducer.
secondly you ought to use the redux state via a mapStateToProps function
function MapDispatchToProps(dispatch) {
return bindActionCreators({ getShifts }, dispatch);
}
function MapStateToProps(state) {
return {
shift: state.start_time OR state.your_reducer.start_time
}
}
export default connect(MapStateToProps, MapDispatchToProps)(StaffRota);
And use this state (that is mapped to prop) via this.props.shift.
I am new to redux and it might be some silly error. I am trying to make an Api call in Action and pass the data to the Reducer. I can see the data passed correctly in the reducer with action.data. I think the problem is in mapStateToProps in the component therefore I am not able to pass the state and render the component. Please find below action - reducers - store.js - home.js
ACTION.JS
export const DATA_AVAILABLE = 'DATA_AVAILABLE';
export function getData(){
return (dispatch) => {
//Make API Call
fetch("MY API URL").then((response) => {
return response.json();
}).then((data) => {
var data = data.articles;
console.log(data)
dispatch({type: DATA_AVAILABLE, data:data});
})
};
}
this is Reducers.JS
import { combineReducers } from 'redux';
import { DATA_AVAILABLE } from "../actions/" //Import the actions types constant we defined in our actions
let dataState = {
data: [],
loading:true
};
const dataReducer = (state = dataState, action) => {
switch (action.type) {
case DATA_AVAILABLE:
state = Object.assign({}, state, {
data: [
...action.data //update current state data reference
],
loading: false
});
console.log(action.data);
return state;
default:
return state;
}
};
// Combine all the reducers
const rootReducer = combineReducers({
dataReducer
// ,[ANOTHER REDUCER], [ANOTHER REDUCER] ....
})
export default rootReducer;
this is Store.js with Redux-thunk
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducers from '../app/reducers/index'; //Import the reducer
// Connect our store to the reducers
export default createStore(reducers, applyMiddleware(thunk));
and finally home.js component when I need to pass the new state and render it
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
FlatList,
View,
Text,
ActivityIndicator
} from 'react-native';
import {bindActionCreators} from 'redux';
import { connect } from 'react-redux';
import * as Actions from '../actions'; //Import your actions
class Home extends Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
this.props.getData(); //call our action
}
render() {
if (this.props.loading) {
return (
<View style={styles.activityIndicatorContainer}>
<ActivityIndicator animating={true}/>
</View>
);
} else {
console.log(this.state)
return (
<View style={styles.row}>
<Text style={styles.title}>
{this.props.data}
fomrmo
</Text>
<Text style={styles.description}>
</Text>
</View>
);
}
}
};
// The function takes data from the app current state,
// and insert/links it into the props of our component.
// This function makes Redux know that this component needs to be passed a piece of the state
function mapStateToProps(state, props) {
return {
loading: state.dataReducer.loading,
data: state.dataReducer.data
}
}
// Doing this merges our actions into the component’s props,
// while wrapping them in dispatch() so that they immediately dispatch an Action.
// Just by doing this, we will have access to the actions defined in out actions file (action/home.js)
function mapDispatchToProps(dispatch) {
return bindActionCreators(Actions, dispatch);
}
//Connect everything
export default connect(mapStateToProps, mapDispatchToProps)(Home);
Assuming that:
case DATA_AVAILABLE:
console.log(action.data.length)
will console.log something more than 0
change your reducer action:
const dataReducer = (state = dataState, action) => {
switch (action.type) {
case DATA_AVAILABLE:
return {
...state,
data: action.data,
loading: false
});
default:
return state;
}
};
To address:
Objects are not valid as a React child(found objects with Keys
{source, author, title, description, url })
that's because you try to render Object:
{this.props.data}
but if you do:
{
this.props.data.map((el, i) =>
<p key={i}>Element nr {i}</p>
)
}
It should work.
I've been bashing my head in trying to play around with this. FOr somereason my props aren't connecting in redux. Here is my error
VM803:36 Warning: Failed prop type: The prop `apiData` is marked as required
in `TestApiPage`, but its value is `undefined`.
in TestApiPage (created by RouterContext)
in RouterContext (created by Router)
in Router (created by Root)
in Provider (created by Root)
in Root
in AppContainer
VM803:36 Warning: Failed prop type: The prop `actions` is marked as required
in `TestApiPage`, but its value is `undefined`.
in TestApiPage (created by RouterContext)
in RouterContext (created by Router)
in Router (created by Root)
in Provider (created by Root)
in Root
in AppContainer
Here is my code
TestApiPage
import React from 'react';
import PropTypes from 'prop-types';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import * as actions from '../actions/testApiActions';
import {TestApiForm} from '../components/TestApi';
export const TestApiPage = (props) => {
console.log(props);
return (
<TestApiForm
apiData={props.apiData}
/>
);
};
TestApiPage.propTypes = {
actions: PropTypes.object.isRequired,
apiData: PropTypes.object.isRequired
};
function mapStateToProps(state) {
return {
apiData: state.apiData
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(TestApiPage);
testApiActions
export function callTestApiAction() {
return function (dispatch) {
return dispatch({
type: types.CALL_TEST_API,
message: "blah",
});
};
}
testApiReducer
import {CALL_TEST_API} from '../constants/actionTypes';
import objectAssign from 'object-assign';
import initialState from './initialState';
export function testApiReducer(state = initialState.apiData, action) {
switch (action.type) {
case CALL_TEST_API:
return objectAssign({}, state, {message: action.message});
default:
return state;
}
}
initialState
export default {
apiData: {
message: "You haven't called your api yet! :("
}
};
When I use Redux dev tools i do see my state there and logs in the connect show it is working. not sure what's going on. Any clues?
So your problem is that your initial state is the contents of apiData, and not an object that contains apiData.
In other words you set state object equal to apiData:
export function testApiReducer(state = initialState.apiData, action) {
// ^ here you set state = apiData
}
But you want to extract apiData as a property of state:
function mapStateToProps(state) {
return {
apiData: state.apiData
// ^ but here you expect state.apiData
};
}
Change the reducer to use the entire initial state instead:
// here's your reducer if your state is `{ apiData }`
export function testApiReducer(state = initialState, action) {
// ^ use only initialState here
switch (action.type) {
case CALL_TEST_API:
// make sure you properly update the internal `apiData` part of the state
return objectAssign({}, state, {
apiData: Object.assign({}, state.apiData, {
message: action.message
});
default:
return state;
}
}
Or change anywhere you have state.apiData to use just state.