I'm trying to call an API with redux action
but everytime I call it in my componentDidMount function, it gives me an error stating that my function is not defined.. i'm so confused, I've been using my past redux project as reference and it's using the same method but it works.
Have a look at my codes
Reducer
import * as types from '../actions/actionconst';
const initialState = {
isfetching: false,
categories: [],
error: null
}
const categoryReducer = (state = initialState, action) => {
switch(action.type){
case types.FETCH_CATEGORIES:
console.log('in fetch categories');
state = {
...state,
isfetching: true,
categories: action.payload
}
break;
case types.FETCH_CATEGORIES_SUCCESS:
state ={...state, categories: action.payload, isfetching: false}
break;
case types.FETCH_CATEGORIES_ERROR:
state = {...state, isfetching: false, error: action.payload}
}
return state;
}
export default categoryReducer
Action
import * as types from './actionconst';
import categoryAPI from '../api/categoryAPI';
export function getCategory(){
return {dispatch => {
fetch("http://localhost:8000/api/v1/categories")
.then((response) => response.json())
.then((responseData) => {
dispatch({
type: types.FETCH_CATEGORIES
payload: responseData
})
})
.catch((err) => {
dispatch({type: types.FETCH_CATEGORIES_ERROR, payload: err});
})
}}
}
Container
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Category from '../components/category';
class CategoryContainer extends Component{
constructor(props){
super(props);
console.log('category props', this.props);
}
componentDidMount(){
console.log('masuk CDM');
this.props.fetchCategory()
}
render(){
var viewtypequery = window.innerWidth >= 1025 ? "computers" : "mobile"
return(
<Category alphabets={this.state.alph}
categorylist={this.state.categoriestemp}
view={viewtypequery}
active={this.state.isActive}
/>
)
}
}
const mapStateToProps = (state) => {
console.log('state is', state);
return{
categories: state.category
}
}
const mapDispatchToProps = (dispatch) => {
return{
fetchCategory: () => {
console.log('cuk ta');
dispatch(getCategory())
}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CategoryContainer)
I dont know if I miss something, It's been a while since I touch this project, been rewatching redux tutorial but I still couldn't find any solutions..
I don't see you importing your getCategory action in your component. I would generally write it like that:
import { getCategory } from '../path-to-action';
.......
export default connect(mapStateToProps, {getCategory})(CategoryContainer)
and then use it directly in the componentDidMount lifecycle method:
componentDidMount(){
this.props.getCategory()
}
Hi Arga try to use bindActionCreators from redux. Make changes in your code to
import React, {Component} from 'react';
import {connect} from 'react-redux';
import Category from '../components/category';
import CategoryActions from '../actions/category'; // notice this will be your category actions file
class CategoryContainer extends Component{
constructor(props){
super(props);
console.log('category props', this.props);
}
componentDidMount(){
console.log('masuk CDM');
this.props.getCategory(); // change here we call function from props binded to category component, this function is defined in your actions file
}
render(){
var viewtypequery = window.innerWidth >= 1025 ? "computers" : "mobile"
return(
<Category alphabets={this.state.alph}
categorylist={this.state.categoriestemp}
view={viewtypequery}
active={this.state.isActive}
/>
)
}
}
const mapStateToProps = (state) => {
console.log('state is', state);
return{
categories: state.category
}
}
const mapDispatchToProps = (dispatch) => {
return bindActionCreators(CategoryActions, dispatch) // notice change here we use bindActionCreators from redux to bind our actions to the component
}
export default connect(mapStateToProps, mapDispatchToProps)(CategoryContainer)
Hopefully it helps.
Related
Hello i begin in react redux i try to play with an api my problem is at begining in my idea i hope make a select and in the select all the results for a day:
My Component ResultListItems:
import { connect } from 'react-redux';
import { fetchResults } from "../actions/index";
class ResultListItems extends Component {
componentWillMount(){
this.props.fetchResults();
}
render() {
return (
<div>
<h2>Coucou la liste resultats</h2>
<select></select>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
results: state.resultsReducer.results
};
};
export default connect(mapStateToProps, null)(ResultListItems)
My Action in index.js in folder actionsat this moment i have a date in url
import axios from "axios";
export const GET_RESULTS = "GET_RESULTS";
const END_POINT = "http://data.nba.net/10s/20200203";
export function fetchResults() {
return function(dispatch) {
axios.get(`${END_POINT}`)
.then(axiosResponse => {
dispatch({ type: GET_RESULTS, payload: axiosResponse.data});
});
}
}
My reducer => reducer_results :
const initialResults ={
results: []
}
export default function (state = initialResults, action) {
switch (action.type) {
case GET_RESULTS:
return {
results: action.payload
};
}
return state
}
I import in index.js in reducer Folder:
import ReducerResults from "../reducers/reducer_results";
const rootReducer = combineReducers({
resultsReducer: ReducerResults
});
export default rootReducer;
And my container is results.js :
import { connect } from 'react-redux';
import ResultListItems from '../components/results_list_item'
class Results extends Component {
render() {
return (
<div>
<h1>App NBA</h1>
<ResultListItems />
</div>
);
}
}
export default connect()(Results);
You are not mapping your api call of fetchResults to props.Try the following. After mapping state to props, map dispatch as well to props in component ResultListItems.
const mapDispatchToProps = (dispatch, ownProps) => {
return {
fetchResults : () => dispatch(fetchResults()),
dispatch
}
}
then coonect it like this.
export default connect(mapStateToProps, mapDispatchToProps)(ResultListItems)
So, you haven't fetchResults in the list of props, this.props.fetchResults is undefined, because you haven't binded the action to the component's props. To deal with it you need to bind the actionCreator. Use a guide: https://blog.benestudio.co/5-ways-to-connect-redux-actions-3f56af4009c8
Or
just do like that:
componentWillMount(){
const {dispatch} = this.props;
dispatch(fetchResults());
}
I'm new to react-redux and i want to dispatch an action from container to component. Here is my code :
Container :
import { connect } from 'react-redux';
import addCountryComponent from '../../views/Country/AddCountry'
import { addCountry } from '../../actions/countryActions'
const mapDispatchToProps = (dispatch) => {
return {
addCountry:(country) => dispatch(addCountry(country))
}
}
const CountryContainer = connect(null, mapDispatchToProps)(addCountryComponent)
export default CountryContainer;
AddCountry Component :
import React, {Component} from 'react';
class AddCountry extends Component {
constructor(props) {
super(props);
}
componentDidMount() {
console.log(this.props);
}
render() {
return (
<div className="animated fadeIn">
asdas
</div>
);
}
}
export default AddCountry;
and Action file
import { ADD_COUNTRY } from './names'
export function addCountry(payload) {
console.log(payload, "actions")
return {
type: ADD_COUNTRY,
payload,
}
}
I can't see addCountry as a props, am i missing something ?
as you mentioned in the comment you should link your container to your route file.
that means your container gets called when a browser open this /add/country
const mapDispatchToProps = dispatch => {
return {
addCountry: (country) => dispatch({ type: ADD_COUNTRY ,country})
}}
TO used it on click event
you should make add reducer for this app
example
const balance =(state={country:""},action)=>{
switch(action.type){
case ADD_COUNTRY: return{ country: action.country;}
this.props.addCountry(country)
This because of mapDispatchToProps auto-add property addCountry to your addCountryComponent.
const mapDispatchToProps = (dispatch) => {
return {
// component that using this function will auto have addCountry in props of component
addCountry:(country) => dispatch(addCountry(country))
}
}
// this addCountryComponent using mapDispatchToProps so it have addCountry in props
const CountryContainer = connect(null, mapDispatchToProps)(addCountryComponent)
export default CountryContainer;
Did you try using bindActionCreators function of Redux?
import { bindActionCreators } from 'redux';
const mapDispatchToProps = (dispatch) => bindActionCreators({
addCountry: addCountry,
}, dispatch);
i have a component and in my component i have some child component.
in my parent component i have some function and i want to trigged it from child component. So i make it with redux.
It's my parent component:
import React, { Component } from "react";
import { withRouter } from "react-router-dom";
import { bindActionCreators } from "redux";
import { splashStop } from "store/actions/Home/splashStop";
import { connect } from "react-redux";
class Home extends Component {
constructor(props) {
super(props);
this.state = {
};
this.goPage = this.goPage.bind(this);
}
componentDidMount() {
}
goPage = () => {
this.props.history.push("/agencies");
};
render() {
if (this.props.homeSplash.splashStart == true) {
myTime.play();
}
return (
<div>
<ChildComponent />
</div>
);
}
}
const mapStateToProps = state => ({
homeSplash: state.homeSplash
});
function mapDispatchToProps(dispatch) {
return {
splashStop: bindActionCreators(splashStop, dispatch)
};
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(withRouter(Home));
it's my child component:
here is in my child component at onClick function i dispatch redux action:
triggerSplash = () => {
this.props.splashStart();
};
my action:
export const START_SPLASH =
"START_SPLASH";
export const splashStart = () => {
return dispatch => {
dispatch({
type: START_SPLASH,
payload: true
});
};
};
and my reducer:
import { START_SPLASH } from "store/actions/Home/splashStart";
let initialState = {
splashStart: false
};
export default (state = initialState, action) => {
switch (action.type) {
case START_SPLASH:
return { ...state, splashStart: action.payload };
default:
return state;
}
};
my reducer, action is working correctly.
here is i wonder why myTime.play(); working always when component mount it's just don't care this control:
if (this.props.homeSplash.splashStart == true) {
myTime.play();
}
i place it to wrong place or what ?
In your redux structure, it seems everything OK. But you should provide your childComponent also to make it more clear.
If you have connected redux action correctly in your child component then try this:
<button ... onClick={() => this.triggerSplash()}>Click</button>
Put arrow function inside onClick. Because, in the component initialization, all component functions are called automatically in the render time.
I'm a bit new to using redux and react. I'm trying to make a simple API call with redux and having it render in react. I can see the API call working as it's in the payload in redux dev tools, but I can't seem to get it to update the state possibly in the `connect?.
actions/index
import FilmAPI from '../api/api';
export const FETCH_FILMS = 'FETCH_FILMS';
export const RECEIVE_FILMS = 'RECEIVE_FILMS';
export const receiveFilms = (films) => {
return {
type: RECEIVE_FILMS,
films
};
}
export const fetchFilmsRequest = () => {
return dispatch => {
return axios.get('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => {
dispatch(receiveFilms(response.data))
})
}
}
export default fetchFilmsRequest;
reducers/FilmReducer
import RECEIVE_FILMS from '../actions/index';
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return [...state, action.films];
default:
return state;
}
}
reducers/index
import { combineReducers } from 'redux';
import { films } from './FilmsReducer';
export default combineReducers({
films,
});
containers/FilmListContainer
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { fetchFilmsRequest } from '../actions';
import FilmList from '../components/FilmList'
class FilmListContainer extends Component {
constructor(props) {
super(props);
}
componentWillMount() {
this.props.fetchFilmsRequest();
}
render() {
return (
<div>
<FilmList films={this.props.films}/>
</div>
);
}
}
const mapStateToProps = state => ({
films: state.films
})
export default connect(mapStateToProps, {fetchFilmsRequest: fetchFilmsRequest})(FilmListContainer);
configureStore.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
const composeEnhancers =
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({
// options like actionSanitizer, stateSanitizer
}) : compose;
const enhancer = composeEnhancers(
applyMiddleware(thunk)
);
return createStore(
rootReducer,
initialState,
enhancer
);
}
As mentioned, Redux DevTools show the films in the payload, but films still remain 0 in its state. Could anyone please point me in the right direction?
You can get updated state by subscribing store and use store.getState()
Steps:
Write subscribe function in constructor of component class.
Set state of class by store.getState().
import store from '../js/store/index';
class MyClass extends Component {
constructor(props, context) {
super(props, context);
this.state = {
classVariable: ''
}
store.subscribe(() => {
this.setState({
classVariable: store.getState().MyStoreState.storeVariable
});
});
}
}
You are close, your action needs to send the data to the store by dispatching an event which your reducer can then catch. This is done using the type attribute on the dispatch object.
https://redux.js.org/basics/actions
return fetch('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => {
dispatch({
type: RECEIVE_FILMS,
payload: response,
})
})
You then need to grab the data in your reducer and put it in the store
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return {
...state,
films: action.payload.films
};
default:
return state;
}
}
It looks like you just need to import your action type constant into your reducer using a named import instead of a default export.
i.e. import {RECEIVE_FILMS} from '../actions' rather than import RECEIVE_FILMS from '../actions'
Just dispatch result of resolved fetch promise like so:
if the payload is json, then:
export const fetchFilmsRequest = () => {
return dispatch => {
return fetch('https://www.snagfilms.com/apis/films.json?limit=10')
.then(response => response.json())
.then(response => {
dispatch({
type: RECEIVE_FILMS,
payload: response
})
})
}
Your reducer would need modifying slightly to:
export function films (state = [], action) {
switch (action.type) {
case RECEIVE_FILMS:
return [...action.payload]; // assuming response is jus array of films
default:
return state;
}
}
I'm developing a react-redux app and I can get access to the reducers via routes. Now I'm facing the trouble of getting access to a specific reducer without using routes.
Here is my reducers.js:
const initialState = {
loading: false,
topics: []
};
export default createReducer(initialState, {
[LOADING_DATA]: (state, action) => {
return Object.assign({}, state, {
loading: action.loading
});
}
});
This is my actions.js:
export function loading (loading) {
return {
type: LOADING_DATA,
payload: {loading}
};
}
And this is what I have on my component:
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux';
import * as moduleActionCreators from '...';
import * as actionCreators from '...';
class MyComponent extends Component {
...
render () {
return (<div>
...
</div>;
}
}
const mapStateToProps = (state) => ({
});
const mapDispatchToProps = (dispatch) => ({
actions: bindActionCreators(Object.assign({}, moduleActionCreators, actionCreators), dispatch)
});
export default connect(mapStateToProps, mapDispatchToProps)(MyComponent);
Normally in the mapStateToProps I reference the reducer variables as loading: state['my_reference_to_reducer'].loading but I can't figure it out how to tell the component to reference my reducers.js in order to get loading as props.
I would appreciate a light on this.
You need to set up the state in mapStateToProps function in order to access it:
const mapStateToProps = (state) => {
return {
loading: state.loading
}
}
Then you should be able to use it as this.props.loading in MyComponent.
Your reducer can look like this:
export default function reducer(state = {}, action) {
switch(action.type) {
case 'LOADING_DATA':
return Object.assign({}, state, {
...state,
loading: action.payload.loading
})
I recommend you to use redux ducks pattern as it keeps action creators and reducers at the same file, saves you time and makes it easier to read and use. For example:
loading.js
// Actions
const LOADING_DATA = 'LOADING_DATA'
// Action Creators
export const loadingData = (data) => {
return {
type: LOADING_DATA,
payload: {
loading: data
}
}
}
// Reducer
export default function reducer(state = {
loading: 'DATA zeroed'
}, action) {
switch(action.type) {
case 'LOADING_DATA':
return Object.assign({}, state, {
...state,
loading: action.payload.loading
})
default:
return state
}
}
index.js
import React from 'react';
import ReactDOM from 'react-dom';
import MyComponent from './MyComponent';
import configureStore from './configureStore'
const store = configureStore()
ReactDOM.render(
<MyComponent store={store}/>,
document.getElementById('root')
);
configureStore.js
import { createStore } from 'redux'
import { composeWithDevTools } from 'redux-devtools-extension'
import loadingData from './loading'
const configureStore = () => {
return createStore(loadingData, composeWithDevTools())
}
export default configureStore
MyComponent.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { loadingData } from './loading';
class MyComponent extends Component {
constructor(props){
super(props)
this.onLoadingData = this.onLoadingData.bind(this)
}
componentDidMount() {
this.props.loadingData('no more undefined')
}
onLoadingData() {
this.props.loadingData('DATA')
}
render() {
console.log(this.props.loading)
return (
<div>
<h2>MyComponent</h2>
<button onClick={this.onLoadingData}>Load Data</button>
<p>{this.props.loading}</p>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.loading
}
}
const mapDispatchToProps = {
loadingData
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(MyComponent)