Try create reducers ang get data from action
But get error in console: reducer is not a function.....
My reducer:
import { INCOME_LIST } from '../actionTypes'
import Immutable from 'immutable'
const initialUserState = {
list: []
}
const listReducer = function(state = initialUserState, action) {
switch(action.type) {
case 'INCOME_LIST':
return Object.assign({}, state, { list: action.data });
}
return state;
}
Where I have mistake?
My Action :
import axios from 'axios'
import { INCOME_LIST } from '../actionTypes'
function receiveData(json) {
return{
type: INCOME_LIST,
data: json
}
};
export function IncomeList () {
return dispatch => {
return (
axios.post('http://139.196.141.166:8084/course/income/outline',{}, {
headers: { 'X-Authenticated-Userid': '15000500000#1' }
}).then(function (response) {
dispatch(receiveData(response.data));
})
)
}
}
How it right way create reducer for that?
Looks like you never exported your reducer. An export default listReducer in your listReducer.js file should do the trick.
Store - holds our state - THERE IS ONLY ONE STATE
Action - State can be modified using actions - SIMPLE OBJECTS
Dispatcher - Action needs to be sent by someone - known as dispatching an action
Reducer - receives the action and modifies the state to give us a new state
pure functions
only mandatory argument is the 'type'
Subscriber - listens for state change to update the ui
const initialState = {
counter: 0
}
const reducer = (state = initialState, action) => {
switch (action.type) {
case 'INCREASE_COUNTER':
return { counter: state.counter + 1 }
case 'DECREASE_COUNTER':
return { counter: state.counter - 1 }
}
return state
}
const store = createStore(reducer)
class App extends Component {
render() {
return (
<Provider store={store}>
<CounterApp />
</Provider>
);
}
}
In my case I called the function immediately
const store = createStore(rootReducer())
instead of
const store = createStore(rootReducer)
Related
I have a redux State HOC to manage the connection
I Have a problem when I add a new post to the store
import React, { useEffect } from "react";
import { connect } from "react-redux";
export default function withState(WrappedComponent) {
function mapStateToProps(reduxState) {
let state = {};
for(let t of Object.entries(reduxState)) {
state = {...state, ...t[1]}
}
return {
...state,
};
}
return connect(
mapStateToProps,
null
)(function (props) {
useEffect(() => {}, [props.posts, props.comments]) /*tried this but didn't work*/
return (
<React.Fragment>
<WrappedComponent {...props} />
</React.Fragment>
);
});
}
I am trying to make the program render the response from my back-end without me reloading the page manually
I tried using the useEffect
and I saw through the dev tools that the state change correctly
my reducer
import { GET_ALL_POSTS, CREATE_NEW_POST } from "../actions"
const initialState = {
posts: []
}
export default function postReducer(state = initialState, action) {
let newState = {...state}
switch(action.type){
case GET_ALL_POSTS:
return {
...newState,
posts: [...action.posts],
}
case CREATE_NEW_POST:
const posts = [...newState.posts, action.post]
return {
...newState,
posts
}
default:
return {
...newState,
}
}
}
I also read that react changes doesn't respond to shallow copies so I changed the whole array in the post reduces when I add a new post
Your withState HOC is very strange. I'm not sure why you don't just use connect directly (or use hooks). But try this:
export function withState(WrappedComponent) {
return connect(
(state) => ({
posts: state.postsReducer.posts,
comments: state.commentsReducer.comments
}),
null
)(WrappedComponent);
}
Not able to access the redux store current state in a Class component.
It shows up console error
Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it.
When I tried to implement the same using a function component with useSelector and useDispatch, everything works as expected. What has gone wrong over here?
reducer.js
let initialState={
count:0
}
const reducer=(state=initialState,action)=>{
switch(action.type){
case ADD_INCREMENT:
return {
...state,
count:state.count+1
};
default: return state;
}
}
export default reducer;
action.js
const Increment=()=>{
return {
type:ADD_INCREMENT
}
}
store.js
import reducer from './reducer';
const store=createStore(reducer);
export default store;
Class Component
import { connect } from 'react-redux';
const mapStateToProps=state=>{
return {
count:state.count
}
}
const mapDispatchToProps=(dispatch)=>{
return {
count:()=>dispatch(action.Increment())
}
}
class Orders extends Component {
render() {
return (
<div>
<h1>Count: {this.props.count} </h1>
</div>
);
}
}
export default connect(mapStateToProps,mapDispatchToProps)(Orders);
In App.js the entire container is wrapped with Provider and store is passed as props
Issue
You've named your state and your action both count, the latter is the one injected as a prop.
const mapStateToProps = state => {
return {
count: state.count // <-- name conflict
}
}
const mapDispatchToProps = (dispatch) => {
return {
count: () => dispatch(action.Increment()) // <-- name conflict
}
}
Solution
Provide different names, count for the state, maybe increment for the action.
const mapStateToProps = state => ({
count: state.count,
});
const mapDispatchToProps = (dispatch) => ({
increment: () => dispatch(action.Increment())
})
I'm a newbie in redux and react.js,
I am trying to make a button disappear on a component in react.js by putting an if condition on the state variable (articlesTable/index.js), which is connected to the redux library function on another file (actions/actionArticles.js), when a button on articlesTable/index.js is clicked, the component is connected with actions/actionArticles.js and dispatch a function in actions/actionArticles.js, which is called loadMoreData().
The function I am trying to configure the state in redux is,
in articlesActions.js
export const loadMoreArticles = () => async (dispatch, getState) => {
const lastArticleKey = Object.keys(getState().articlesMap).pop();
const lastArticle = getState().articlesMap[lastArticleKey];
console.log("articleMap", getState().articlesMap);
console.log("Last article", lastArticleKey, lastArticle);
let filteredArticles = {};
const uid = getState().auth.uid;
const userLevel = getState().profile.userLevel;
} else {
const filteredArticlesArray = [];
var lastArticleReached = false;
...
var lastArticleInArray = filteredArticlesArray[filteredArticlesArray.length-1];
if (lastArticleInArray[0]===lastArticleKey) {
console.log("Bingo, last article reached!");
lastArticleReached = true;
}
else if (lastArticleInArray[0]!== lastArticleKey)
{
console.log("Not last article");
lastArticleReached = false;
}
filteredArticles = Object.fromEntries(filteredArticlesArray.reverse());
}
dispatch({type: LAST_ARTICLE_REACHED, payload: lastArticleReached})
...
};
I dispatch this function with
dispatch({ type: LOAD_MORE_ARTICLES, payload: filteredArticles });
in the code snippet above
The root reducer looks like this,
reducers/index.js
import { combineReducers } from 'redux';
import { reducer as formReducer } from 'redux-form';
import articlesStatusReducer from './articlesStatusReducer';
const rootReducer = combineReducers({
...
articlesStatus: articlesStatusReducer,
form: formReducer,
...
});
export default rootReducer;
In articleStatusReducer,
import {LAST_ARTICLE_REACHED} from "../actions/types";
export default function(state = {}, action) {
switch(action.type) {
case(LAST_ARTICLE_REACHED):
console.log(action.payload);
return action.payload;
default:
return state;
}
}
In the articlesTable/index.js, I connect like this
const mapStateToProps = (state) => {
return {
articlesMap: state.articlesMap,
appStatus: state.appStatus,
profile: state.profile,
lastArticleReached: state.articlesStatus,
}
};
const mapDispatchToProps = (dispatch) => {
return {
getArticlesWithData: () => dispatch(getArticlesWithData()),
loadMore: () => dispatch(loadMoreArticles())
}
};
export default compose(
withRouter,
connect(mapStateToProps, mapDispatchToProps)
)(ArticlesTable)
For some reason, articleStatus isn't recognised and when I do
console.log(this.props.articleStatus)
state.articleStatus is undefined
How can I reference state.articleStatus which should be boolean ?
Edit:
For some reason when I put it in a conditional JSX brackets in the render method, it prints out undefined
render () => {
{
console.log(this.props.lastArticleReached),
!this.props.lastArticleReached
: <Button> </Button>
?
<div><div>
}
}``
In function mapStateToProps, you should map state.articleStatus to a props.
somethings like this:
const mapStateToProps = (state) => {
return {
articlesMap: state.articlesMap,
appStatus: state.appStatus,
profile: state.profile,
lastArticleReached: state.articlesStatus,
articleStatus: state.articleStatus
}
};
So this.props.articleStatus will works . :)
The problem is in your reducer. Each case of your reducer must return the state but in your case, your return action.payload.
try something like this.
case(LAST_ARTICLE_REACHED):
console.log(action.payload);
return {...state, articleStatus: action.payload};
like this, articlesStatus became an object with one props, articleStatus, your boolean.
I tried another name for the props but with similar method as Thomas Caillard,
Reducer.js
case(REACH_LAST_ARTICLE):
return {...state, lastArticleReached: action.payload}
in component index.js
const mapStateToProps = (state) => {
return {
...
lastArticleReached: state.articlesMap.lastArticleReached
...
}
};
Thanks for all the helps so far
So i'm doing a API GET request and set the data on reducer, but the component render twice, first before dispatch and another after, the first one is causing map function problem
what can i do to avoid render twice and solve map function problem?
App.js
componentDidMount(){
this.props.carregarLojas();
}
render(){
const { lojasTeste } = this.props;
//rendering 2 times
console.log(lojasTeste);
return(
<div>
lojasTeste.map((i, index) => (
<h1>{i.name}</h1>
))
</div>
)
}
const mapStateToProps = store => ({
lojasTeste: store.lojaState.lojasTeste
});
const mapDispatchToProps = dispatch => {
return {
carregarLojas: () => {
dispatch(carregarLojas());
}
};
};
export default connect(mapStateToProps, mapDispatchToProps)(App);
Action.js
export const setarLojas = (lojas) =>{
return {
type: SETAR_LOJAS,
data: lojas
}
}
export const carregarLojas = () => {
return (dispatch) => {
return API.get('loja')
.then(response => {
dispatch(setarLojas(response.data))
})
.catch(error => {
throw(error);
})
}
Reducer.js
const initialState ={
lojasTeste: {}
}
export const lojaReducer = (state = initialState, action) => {
switch (action.type){
case SETAR_LOJAS:
return {
...state,
lojasTeste: action.data
}
default:
return state;
}
}
The double render is totally normal:
Your component render once, then call the carregarLojas method which is async. When resolved, the method will update your redux store, which is connected with the props of your component (mapStateToProps). When a prop is updated, it cause automatically a rerender.
Also, for your map problem, you didn't initialized lojasTeste as an array, but as an object. You can't use map on an object (cf https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/Array/map)
I am using React redux with firebase realtime database.
In App.js I am dispatching an action fetchAllPosts
App.js
class App extends Component {
componentDidMount() {
this.props.fetchAllPosts();
}
render() {
return (
<div className="App">
// something ...
</div>
);
}
}
const mapDispatchToProps = dispatch => {
return {
fetchAllPosts: () => {dispatch(allPosts())}
}
}
My action looks like this (I am using redux-thunk):
action
export function allPosts() {
return (dispatch) => {
firebase.database().ref('posts/').on('value', (snapshot) => {
dispatch({type: "ALL_POSTS", postsArray: snapshot.val(), loading: false})
})
}
}
Then I am combining reducers (I know in this case it is not necessary):
const rootReducer = combineReducers({
allPosts: postsReducer
})
My reducer looks like this:
reducer
const initialState = {
allPosts: []
}
const postsReducer = (state = initialState, action) => {
switch(action.type) {
case "ALL_POSTS" :
console.log("action payload all posts", action.postsArray)
return {
...state,
loading: false,
allPosts: action.postsArray
}
break;
default:
return state
}
return state
}
And finally: my SinglePostview component looks like this:
SinglePostview.js
import React, {Component} from 'react';
import {connect} from 'react-redux';
class SinglePostview extends Component {
render() {
console.log("ppp", this.props)
return (
<h2>{this.props.post.title}</h2>
)
}
}
const mapStateToProps = (state, ownprops) => {
const postId = ownprops.match.params.postid
return {
post: state.allPosts.allPosts[postId]
}
}
export default connect(mapStateToProps)(SinglePostview);
Here when the render method is executing, this.props.post is undefined and I have the error:
TypeError: Cannot read property 'title' of undefined.
The problem is: when the app loads for the first time, props.post is undefined (so I have an error) and after about 1 second it receives the value but it doesn't change anything - the error still exists and the value is not displaying.
Could anyone help me?
Assuming your reducer is fine, you can fix this by
changing this
render() {
return (
<h2>{this.props.post.title}</h2>
)
}
To this:
render() {
if (!this.props.post){
return null;
}
return (
<h2>{this.props.post.title}</h2>
)
}
or
render() {
return (
<h2>{this.props.post && this.props.post.title}</h2>
)
}
You are defining allPosts to be an array
const initialState = {
allPosts: []
}
But you are trying to access it like an object.
state.allPosts.allPosts[postId]
Hence, if your state.allPosts.allPosts is an array , try using the ES6 find() method to get a post from the array with the postId.
Assuming
state.allPosts.allPosts = [
{postId: 1,title:'abcd'},
{postId:2,title:'def'}
]
state.allPosts.allPosts.find(post => postId === post.postId)