I was fetching data from API it's working fine, using react-native with expo I'm using redux it was working file with single reducer but when I used combined reducers it's stops rendering but still able to log all data in console, I'm not sure what I'm doing wrong I have been facing this issue for days.
I have tried:
reinstalling expo
upgrading expo
recreating new project
I have searched the internet but I have been unable to find any solution
Reducer
import {
PRODUCT_DETAILS_REQUEST,
PRODUCT_DETAILS_SUCCESS,
PRODUCT_DETAILS_FAIL,
} from "../../../constants";
const initialState = [];
const productDetailsReducer = (state = initialState, action) => {
switch (action.type) {
case PRODUCT_DETAILS_REQUEST:
return { loading: true };
case PRODUCT_DETAILS_SUCCESS:
return { loading: false, product: action.payload };
case PRODUCT_DETAILS_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
export default productDetailsReducer;
component
import React, { useEffect } from 'react'
import { StyleSheet, ScrollView, Linking, Text, View, Image } from "react-native";
import { Card, Button } from "react-native-elements";
import { useSelector, useDispatch } from 'react-redux'
import { getProduct } from '../Redux/actions/products.Action.js'
export const Product = ({ route }) => {
const { itemId } = route.params;
// 376
const dispatch = useDispatch()
const storeState = useSelector((state) => state.productDetailsReducer);
const {product, loading, error} = storeState;
const printdata = () => {
console.log(product.name);
}
useEffect(() => {
dispatch(getProduct(itemId));
}, [dispatch]);
return (
<>
<Text> {loading ? "loading" : product.name}</Text>
<Button onPress={() => { printdata() }}>press</Button>
</>
)
}
export default Product
error
state
change
const initialState = [] to const initialState = {}
const printdata = () => {
console.log(product?.name); // check is product exists then console.log(product.name)
}
Related
I'm trying to get initial data from a reducer by dispatching action from App.js component, it works fine but when I switch to another component and load it with useSelector it gets undefined.
I have tried this line of code in Headphones.js but the second one returns undefined
const allData = useSelector((state) => state.allDataReducer);
const { loading, error, data } = allData;
App.js
const dispatch = useDispatch();
useEffect(() => {
dispatch(welcomeAction());
dispatch(listAllData);
}, [dispatch]);
allDataReducer.js
import {
LIST_ALL_DATA_FAIL,
LIST_ALL_DATA_REQUEST,
LIST_ALL_DATA_SUCCESS,
} from "../constants/shared";
export const allDataReducer = (state = { loading: true, data: {} }, action) => {
switch (action.type) {
case LIST_ALL_DATA_REQUEST:
return { loading: true };
case LIST_ALL_DATA_SUCCESS:
return { loading: false, data: action.payload };
case LIST_ALL_DATA_FAIL:
return { loading: false, error: action.payload };
default:
return state;
}
};
shared.js
import {
LIST_ALL_DATA_FAIL,
LIST_ALL_DATA_REQUEST,
LIST_ALL_DATA_SUCCESS,
} from "../constants/shared";
import Axios from "axios";
export const listAllData = async (dispatch) => {
dispatch({
type: LIST_ALL_DATA_REQUEST,
});
try {
const { data } = await Axios.get("/all");
dispatch({ type: LIST_ALL_DATA_SUCCESS, payload: data });
} catch (error) {
dispatch({ type: LIST_ALL_DATA_FAIL, payload: error.message });
}
};
Headphones.js
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { listheadphones } from "../actions/headphonesActions";
import BasicSection from "../components/BasicSection";
import Definer from "../components/Definer";
import LoadingBox from "../components/LoadingBox";
import MessageBox from "../components/MessageBox";
import ProductsCategories from "../components/ProductsCategories";
import BestAudioGear from "../components/BestAudioGear";
const Headphones = (props) => {
const dispatch = useDispatch();
const headphonesList = useSelector((state) => state.headphonesList);
const allData = useSelector((state) => state.allData);
const { loading, error, data } = allData; //undefined
//const { loading, error, headphones } = headphonesList;
console.log(headphonesList);
useEffect(() => {
dispatch(listheadphones());
}, [dispatch]);
return (
<div>
<Definer title="HEADPHONES" />
{loading ? (
<LoadingBox></LoadingBox>
) : error ? (
<MessageBox variant="danger">{error}</MessageBox>
) : (
headphones.map((headphone) => (
<BasicSection
key={headphone.id}
name={headphone.headerName}
info={headphone.info}
mobile={headphone.mobile}
tablet={headphone.tablet}
desktop={headphone.desktop}
/>
))
)}
<ProductsCategories />
<BestAudioGear />
</div>
);
};
export default Headphones;
Github repo
Your description is still not specific enough, can't really pin down what the issue is. But here is some stuff I noticed:
dispatch(listAllData); somehow looks wrong to me, the action creator is usually a function that gets called: dispatch(listAllData());
Then where you define export const listAllData = async (dispatch) => { - this should be a function that returns a function if you're using the thunk middleware. You only defined a function.
I have a problem. As I understood hook useEffect doen't run.
I have action that should take data from server.
export const getProducts = () => {
return dispatch => {
dispatch(getProductsStarted());
fetch('https://shopserver.firebaseapp.com/get-products')
.then(res => {
dispatch(getProductsSuccess(res.json()));
})
.catch(err => {
dispatch(getProductsFailure(err.message));
});
}
}
const getProductsSuccess = todo => ({
type: "ADD_TODO_SUCCESS",
payload: {
...todo
}
});
const getProductsStarted = () => ({
type: "ADD_TODO_STARTED"
});
const getProductsFailure = error => ({
type: "ADD_TODO_FAILURE",
payload: {
error
}
});
I have a reducer.
const initialState = {
loading: false,
products: [],
error: null
}
export const ProductReducer = (state = initialState, action) => {
switch (action.type) {
case "ADD_TODO_SUCCESS":
return {
...state,
loading: false,
error: null,
todos: [...state.products, action.payload.products]
}
case "ADD_TODO_STARTED":
return {
...state,
loading: true
}
case "ADD_TODO_FAILURE":
return {
...state,
loading: false,
error: action.payload.error
}
default:
return state
}
}
And I have a Component where I want to render a result.
import React from 'react';
import { CardItem } from "./cardItem";
import { useSelector } from 'react-redux';
import { useEffect } from 'react';
import { getProducts } from '../Redux/Actions/productAction'
export const ProductCard = () => {
useEffect(() => {
getProducts();
console.log('111111')
})
const data = useSelector(state => state.ProductReducer.products);
return (
<div>
{data.map( element =>
CardItem (element)
)}
</div>
)
}
After rendering page nothing happens. ReduxDevTools shows that there was no send actions. Please, help me to fix it. Thank you.
I think you should be calling your async action like this :
import { useDispatch, useSelector } from 'react-redux';
[...]
export const ProductCard = () => {
const dispatch = useDispatch();
useEffect(() => {
// I guess getProducts is an async redux action using redux-thunk
dispatch(getProducts());
console.log('111111')
}, []);
[...]
}
I assume you want to load products only when component is born, so I pass an empty array as second argument for useEffect (https://reactjs.org/docs/hooks-reference.html#useeffect).
I am trying to create a simple application with a context provider, when running the application on my phone using Expo application, it throws an error like in this photo
here is my app.js file:
import React from 'react';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import IndexScreen from "./src/screens/IndexScreen";
import {BlogProvider} from "./src/context/BlogContext";
const navigator = createStackNavigator({
Index: IndexScreen
}, {
initialRouteName: 'Index',
defaultNavigationOptions: {
title: 'Blog List'
}
});
const App = createAppContainer(navigator);
export default () => {
return <BlogProvider><App/></BlogProvider>;
}
and the blogContext.js file:
import React, {useReducer} from 'react';
const BlogContext = React.createContext();
const reducer = (state, action) => {
switch (action.type) {
case 'add':
return [...state, `Blog post #${state.length + 1}`]
default:
return state;
}
}
export const BlogProvider = ({children}) => {
const [state, dispatch] = useReducer(reducer,[]);
const addBlogPost = () => {
dispatch({type: 'add'})
}
return <BlogContext.Provider value={{data: state, addBlogPosts: addBlogPost}}> {children} </BlogContext.Provider>
}
export default BlogContext;
I am seeing this error only on mobile, when opening the app in the browser it doesnt show any errors
the problem was the return statement must be wrapped into parentheses like so:
import React, {useReducer} from 'react';
const BlogContext = React.createContext();
const blogReducer = (state, action) => {
switch (action.type) {
case 'add':
return [...state, `Blog post #${state.length + 1}`]
default:
return state;
}
}
export const BlogProvider = ({children}) => {
const [blogPosts, dispatch] = useReducer(blogReducer,[]);
const addBlogPost = () => {
dispatch({type: 'add'})
};
return (
<BlogContext.Provider value={{data: blogPosts, addBlogPost}}>
{children}
</BlogContext.Provider>
);
};
export default BlogContext;
I have just wasted 3 hours of my life contemplating quitting code because of this.
This might be a possible solution for someone else.
I rewrote everything and I realized that my linter added a space before and after {children}.
i.e.:
//CORRECT
return (<UserContext.Provider value={{ userAuth, userDocument }}>{children}</UserContext.Provider>);
//WRONG will give Error: Text string must be rendered...blah blah..
return (<UserContext.Provider value={{ userAuth, userDocument }}> {children} </UserContext.Provider>);
I can't read database to insert in 'Text'. I'm using Redux
Json
DiasSemana
|
|__Quarta: "Test Program"
|
|__Quinta: "nothing"
|
|
Class EstudoDia.js I used mapStateToProps, connect and import actions.
import React, { Component } from "react";
import { View, Text } from "react-native";
import { Actions } from "react-native-router-flux";
import { connect } from "react-redux";
import firebase from "firebase";
import { estudoDoDia } from "../actions/AutenticacaoActions";
export class estudoDia extends Component {
componentWillMount() {
//I've tried with 'this.props.quarta' and it didn't work
props.estudoDia(props.quarta);
}
render() {
return (
<View>
<Text>{props.quarta}</Text>
</View>
);
}
}
const mapStateToProps = state => ({
quarta: state.AutenticacaoReducer.quarta
});
export default connect(mapStateToProps, { estudoDoDia })(estudoDia);
types.js
export const MATERIA_DO_DIA = 'materia_do_dia';
ActionReducer.js. I import types and return payload
import { MATERIA_DO_DIA } from "../actions/types";
const INITIAL_STATE = {
quarta: "T.I Program"
};
export default (state = INITIAL_STATE, action) => {
console.log(action);
switch (action.type) {
case MATERIA_DO_DIA:
return { ...state, quarta: action.payload };
default:
return state;
}
};
AutenticacaoActions.js. Used snaphot
import firebase from "firebase";
import { Actions } from "react-native-router-flux";
import { MATERIA_DO_DIA } from "./types";
export const estudoDoDia = quarta => {
return dispatch => {
firebase.database
.ref("/DiasSemana/Quarta")
.once("value")
.then(snapshot => {
quarta = snapshot.val();
dispatch({
type: MATERIA_DO_DIA,
payload: snapshot.val
});
});
};
};
have image in link
enter image description here
thank you if you can help me
I thank you all for your help, in case I discovered that the problem is in the estudoDoDia method in AuthenticationActions.js, I made this change and it worked
export const estudoDoDia = (quarta) => {
return dispatch => {
firebase.database().ref('DiasSemana/Quarta').once('value', function (snapshot) {
console.log(snapshot.val())
quarta = snapshot.toJSON();
dispatch(
{
type: MATERIA_DO_DIA,
payload: quarta
})
})
}
}
Inside once I made it call a function passing the snapshop as a parameter and it worked.
I thank you for your help and thank you all
I recently started using redux for a new personal project. It worked pretty well until I started using "combineReducers". Whenever I click "Fetch todos" both my user as well as my todo reducer get updated and even though they have different data field names both get the same data. Now I probably did some wrong encapsulation here. But no matter how often I went over the docs, I just cannot see what I am doing wrong.
My store initialization script:
import { createStore, combineReducers, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import toDoReducer from './todos/reducer';
import userReducer from './users/reducer';
const rootReducer = combineReducers({
todosSlice: toDoReducer,
usersSlice: userReducer
});
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));
export default store;
gets injected into index:
import React from 'react';
import ReactDOM from 'react-dom';
import App from './containers/app/App';
import * as serviceWorker from './serviceWorker';
import { Provider } from 'react-redux';
import configureStore from './store/configureStore';
ReactDOM.render(<Provider store={ configureStore }><App /></Provider>, document.getElementById('root'));
serviceWorker.unregister();
My app hold the logic for the todo container
import React, { Component } from 'react';
import { connect } from 'react-redux';
import * as todoActions from '../../store/todos/actions';
import UserContainer from '../usersContainer/UserContainer';
class App extends Component {
componentDidMount() {
console.log(this.props);
}
render() {
let loading = '';
let error = '';
let todos = [];
// check whether the component is fetching data
this.props.loading === true ? loading = <p>Loading...</p> : loading = '';
// check if there was an error
this.props.error && this.props.loading === false ? error = <p>There was an error</p> : error = '';
// map the todos in the desired html markup.
todos = this.props.todos.map( todo => {
return <div key={todo.id}> name: {todo.title} </div>
});
return (
<div className="App">
{/* <UserContainer /> */}
{loading}
{error}
<p onClick={() => this.props.onFetchTodos()}>Fetch Todos</p>
{todos}
</div>
);
}
}
const mapStateToProps = state => {
return {
error: state.todosSlice.error,
loading: state.todosSlice.loading,
todos: state.todosSlice.todos
}
}
const mapDispatchToProps = dispatch => {
return {
onFetchTodos: () => dispatch(todoActions.fetchTodos())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(App);
Which has the following actions:
import axios from 'axios';
export const FETCH_TODOS = 'FETCH_TODOS';
export const GET_TODOS_STARTED = 'GET_TODOS_STARTED';
export const FETCH_TODOS_SUCCESS = 'FETCH_TODOS_SUCCESS';
export const FETCH_TODOS_FAILURE = 'FETCH_TODOS_FAILURE';
export const fetchRequest = () => {
return dispatch => {
dispatch(getTodoStarted());
axios.get('https://one365-api-dev.azurewebsites.net/api/teams/')
.then(result => {
dispatch(fetchTodosSucces(result));
}).catch(error => {
dispatch(fetchTodoFailure(error));
});
}
}
const getTodoStarted = () => ({
type: GET_TODOS_STARTED
});
const fetchTodosSucces = todos => ({
type: FETCH_TODOS_SUCCESS,
payload: {
...todos
}
});
const fetchTodoFailure = error => ({
type: FETCH_TODOS_FAILURE,
payload: {
error
}
});
export const fetchTodos = () => {
return (dispatch => {
dispatch(fetchRequest());
});
}
And it's reducer
import * as actions from './actions';
const initialState = {
error: null,
loading: false,
todos: []
}
const todosReducer = (state = initialState, action) => {
switch(action.type) {
case actions.GET_TODOS_STARTED: {
console.log('fetch todo state', state)
return {
...state,
loading: state.loading = true
};
}
case actions.FETCH_TODOS_SUCCESS: {
const todos = action.payload.data;
return {
...state,
loading: false,
todos: state.todos = todos
};
}
case actions.FETCH_TODOS_FAILURE: {
const error = action.payload.error;
return {
...state,
loading: false,
error: state.error = error
};
}
default: {
return state;
}
}
}
export default todosReducer;
The Users Component
import React from 'react';
import { connect } from 'react-redux';
import * as userActions from '../../store/users/actions';
class UserContainer extends React.Component {
render () {
let loading = '';
let error = '';
let users = [];
// check whether the component is fetching data
this.props.usersLoading === true ? loading = <p>Loading...</p> : loading = '';
// check if there was an error
this.props.usersError && this.props.loading === false ? error = <p>There was an error</p> : error = '';
// map the users in the desired html markup.
users = this.props.users.map( user => {
return <div key={user.id}> name: {user.title} </div>
});
return (
<div className="Users">
{loading}
{error}
<p onClick={() => this.props.onFetchUsers()}>Fetch Users</p>
{users}
</div>
);
}
}
const mapStateToProps = state => {
return {
usersError: state.usersSlice.error,
usersLoading: state.usersSlice.loading,
users: state.usersSlice.users
}
}
const mapDispatchToProps= (dispatch) => {
return {
onFetchUsers: () => dispatch(userActions.fetchUsers())
}
}
export default connect(mapStateToProps, mapDispatchToProps)(UserContainer);
the user actions:
import axios from 'axios';
export const FETCH_USERS = 'FETCH_TODOS';
export const FETCH_USERS_STARTED = 'GET_TODOS_STARTED';
export const FETCH_USERS_SUCCESS = 'FETCH_TODOS_SUCCESS';
export const FETCH_USERS_FAILURE = 'FETCH_TODOS_FAILURE';
export const fetchRequest = () => {
return dispatch => {
dispatch(fetchUsersStarted());
axios.get('https://one365-api-dev.azurewebsites.net/api/me')
.then(result => {
dispatch(fetchUsersSuccess(result));
}).catch(error => {
dispatch(fetchUsersFailure(error));
});
}
}
export const fetchUsersSuccess = (users) => {
return {
type: FETCH_USERS_SUCCESS,
payload: {
...users
}
}
}
export const fetchUsersStarted = () => ({
type: FETCH_USERS_STARTED
});
export const fetchUsersFailure = (error) => {
return {
type: FETCH_USERS_FAILURE,
payload: {
error
}
}
}
export const fetchUsers = () => {
return dispatch => {
dispatch(fetchRequest())
}
};
And it's reducer:
import * as actions from './actions';
const initialState = {
error: '',
loading: false,
users: []
}
const userReducer = (state = initialState, action) => {
switch(action.type) {
case actions.FETCH_USERS_STARTED: {
console.log('fetch users state', state)
return {
...state,
loading: state.loading = true
}
}
case actions.FETCH_USERS_SUCCESS: {
const users = action.payload.data;
return {
...state,
loading: false,
users: state.users = users
}
}
case actions.FETCH_USERS_FAILURE: {
const error = state.payload.error;
return {
...state,
loading: false,
error: state.error = error
}
}
default: {
return state;
}
}
}
export default userReducer;
Now when I run my DEV server I only see the fetch todo button. I commented out the users on click handler to see if it was an event bubble going up. Bu t this wasn't the case.
Once the app load redux dev tools shows the state as follows:
but once i click the fetch todo's handler. Both todos and users get filled.
I appreciate anyone who read though so much (boilerplate) code. I probably made a problem encapsulating my state. but again after reading many tutorials I still cannot find my issue.
You have a copy/paste issue. You changed the names of the constants for your "USERS" actions, but left the values the same as the "TODOS" actions.
export const FETCH_USERS = 'FETCH_TODOS';
export const FETCH_USERS_STARTED = 'GET_TODOS_STARTED';
export const FETCH_USERS_SUCCESS = 'FETCH_TODOS_SUCCESS';
export const FETCH_USERS_FAILURE = 'FETCH_TODOS_FAILURE';
I assume you meant to have:
export const FETCH_USERS = 'FETCH_USERS';
export const FETCH_USERS_STARTED = 'FETCH_USERS_STARTED';
export const FETCH_USERS_SUCCESS = 'FETCH_USERS_SUCCESS';
export const FETCH_USERS_FAILURE = 'FETCH_USERS_FAILURE';