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
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());
}
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.
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 trying to learn react-redux architecture, and I failed on the most basic stuff.
I created class HomePage and used react-redux connect() to connect it to store's state and dispatch.
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import {HomeButtonClickAction} from "./HomeActionReducer";
import {connect} from "react-redux";
class HomePage extends Component {
constructor(props) {
super(props);
console.log('HomePage props');
console.log(this.props);
this.buttonClicked = this.buttonClicked.bind(this);
}
buttonClicked() {
console.log('button cliked');
this.props.buttonClick();
}
render() {
console.log('Re-rendering...');
let toggleState = this.props.toggle ? 'ON' : 'OFF';
return (
<div>
<button onClick={this.buttonClicked}>{ toggleState }</button>
</div>
)
}
}
HomePage.propTypes = {
toggle: PropTypes.bool.isRequired,
onClick: PropTypes.func.isRequired
};
const mapStateToProps = (state, ownProps) => {
return {
toggle: state.toggle
}
};
const mapDispatchToProps = (dispatch, ownProps) => {
return {
buttonClick: () => {
dispatch(HomeButtonClickAction());
}
}
};
const HomeContainer = connect(
mapStateToProps,
mapDispatchToProps
)(HomePage);
export default HomePage;
But it's not working for me. HomeContainer doesn't pass props to HomePage component.
I've got these warnings in devtools.
My index.js looks like this.
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import registerServiceWorker from './registerServiceWorker';
import AppReducer from "./reducers/AppReducer";
import { createStore } from "redux";
import { Provider } from 'react-redux';
const store = createStore(AppReducer);
ReactDOM.render(
<Provider store={ store }>
<App/>
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
and AppReducer.js
import { combineReducers } from 'redux';
import { toggle } from '../home/HomeActionReducer';
const AppReducer = combineReducers({
toggle
});
export default AppReducer;
and HomeActionReducer.js
const HOME_BUTTON_CLICK = 'HOME_BUTTON_CLICK';
export function toggle (state = true, action) {
console.log('toggle launched');
switch (action.type) {
case HOME_BUTTON_CLICK :
return !state;
default:
console.log('Toggle reducer default action');
return state;
}
}
export function HomeButtonClickAction() {
console.log('action emitted');
return {
type: HOME_BUTTON_CLICK
};
}
Being a newbie I'll really appreciate your help :)
You are exporting HomePage, which is the presentational component. You want to export HomeContainer, which is the container that passes the props to HomePage through connect.
So replace this
export default HomePage;
with this
export default HomeContainer;
You can also directly write
export default connect(mapStateToProps, mapDispatchToProps)(HomePage);
Note that, since it's the default export, you can name the import as you want, eg.:
import HomePage from './HomePage' // even if it's HomeContainer that is exported
You have this:
const HomeContainer = connect(
mapStateToProps,
mapDispatchToProps
)(HomePage);
export default HomePage;
To create an instance of the connect component you need to do this:
export default connect()(HomePage);
Notice I did not write export default twice, bad practice, you only export default once per component so the connect() goes inside that same line of code and the invocation or second set of parentheses you wrap around the component you are working in.
This connect() function is actually a React component that you are going to pass some configuration to and the way you begin to do that is by calling mapStateToProps like so:
const mapStateToProps = () => {
};
export default connect()(HomePage);
You could also do:
function mapStateToProps() {
}
If you read it, it makes sense, this is saying that we are going to map our state object, all the data inside the redux store and run some computation that will cause that data to show up as props inside our component, so thats the meaning of mapStateToProps.
Technically, we can call it anything we want, it does not have to be mapStateToProps, but by convention we usually call it mapStateToProps and its going to be called with all the state inside of the redux store.
const mapStateToProps = (state) => {
};
export default connect()(HomePage);
The state object contains whatever data you are trying to access from the redux store. You can verify this by console logging state inside the function like so:
const mapStateToProps = (state) => {
console.log(state);
return state;
};
export default connect()(HomePage);
I am returning state just to ensure that everything is working just fine.
After defining that function, you take it and pass it as the first argument to the connect() component like so:
const mapStateToProps = (state) => {
console.log(state);
return state;
};
export default connect(mapStateToProps)(HomePage);
Thats how we configure the connect component.We configure it by passing it a function. Run that and see what happens.