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);
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>
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 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.
Background
I am working on a very routine chunk of code, I have created actions and reducers many times throughout my app. I am now setting up authentication, and have two containers loading based on routes / & /register.
Issue
I am trying to dispatch an action, and do a simple console.log("test"). I have done this many times before, in-fact, I have literally duplicated a container and altered the names of the dispatched action names. One container works, while the other is hitting me with:
Uncaught TypeError: _this2.propsregisterHandler is not a function
I am confused why its not showing a . between props and registerHandler
Here is the relevent code:
Container Import
import { register } from "../../store/actions/authentication";
JSX
<div
className="btn btn-primary col"
onClick={() =>
this.props.registerHandler(
this.state.email,
this.state.password
)
}
>
Register
</div>
....
Disptach Code
const mapStateToProps = state => {
return {};
};
const mapDisptachToProps = dispatch => {
return {
registerHandler: () => dispatch(register())
};
};
export default connect(
mapStateToProps,
mapDisptachToProps
)(Register);
The action
import * as actionTypes from "./actiontypes";
export const register = () => {
console.log("TEST");
return { type: actionTypes.REGISTER };
};
Reducer
const reducer = (state = initialState, action) => {
switch (action.type) {
case actiontypes.REGISTER: {
console.log("you called the reducer");
return state;
}
Revised
This code here does not work, I always get the error, however if I call the same action in my login component, it will work.
import React, { Component } from "react";
import { connect } from "react-redux";
import { registerUserToApp } from "../../store/actions/authentication";
import "../Login/login";
export class Register extends Component {
state = {
email: "",
password: ""
};
render() {
return (
<div
className="btn btn-primary"
onClick={() => {
this.props.registerUserToAppHandler();
}}
>
Register
</div>
);
}
}
const mapStateToProps = state => {
return {};
};
const mapDispatchToProps = dispatch => {
return {
registerUserToAppHandler: () => dispatch(registerUserToApp())
};
};
export default connect(
mapDispatchToProps,
mapStateToProps
)(Register);
login Component
import React, { Component } from "react";
import { connect } from "react-redux";
import Aux from "../../components/hoc/Aux";
import Logo from "../../assets/images/Logo.png";
import GoogleLogo from "../../assets/images/google.svg";
import {
loginUser,
loginUserWithGoogle,
registerUserToApp
} from "../../store/actions/authentication";
import "./login.css";
export class Login extends Component {
state = {
email: "",
password: ""
};
render() {
const userNameChangeHandler = event => {
this.setState({
email: event.target.value
});
};
const passworChangeHandler = event => {
this.setState({
password: event.target.value
});
};
return (
<Aux>
...
<div
className="btn btn-primary col"
onClick={() => {
this.props.loginUserHandler(
this.state.email,
this.state.password
);
this.props.registerUserToAppHandler();
}}
>
Sign In
</div>
...
</Aux>
);
}
}
const mapStateToProps = state => {
return {};
};
const mapDisptachToProps = dispatch => {
return {
loginUserHandler: (email, password) => dispatch(loginUser(email, password)),
registerUserToAppHandler: () => dispatch(registerUserToApp()),
loginUserWithGoogleHandler: () => dispatch(loginUserWithGoogle())
};
};
export default connect(
mapStateToProps,
mapDisptachToProps
)(Login);
I can't leave a comment, but shouldn't you add .css extension when importing styles?
import "../Login/login";
The issue was due to how I was loading this component into my container. I am nut sure of the exact reasoning but I was importing my component into the container using a named import import {Login} from ".../path", whereas it should have been import Login from ".../path".
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.