Undefined Data exporting from contextstate - react-hooks

I'm creating a web app to create post,post comments,edit,deleete, I'm having problem while destructuring data from context i used, it is destructuring as undefined can anyone pease, i'm stuck on this since two days;
postContext.js
import { createContext } from "react";
const postContext = createContext();
export default postContext;
PostState.js
import PostContext from "./postContext";
import { useState } from "react";
const PostState=(props)=>{
const postsinitial =[
{
"_id": "63ed1aa3159fb228fe733188",
"posti": "This is my first note Noopur",
"comment": [
"Hello Noopue this is my first post",
"hello pratik",
"how are you",
"I'm fine pratik"
],
"timestamp": "2023-02-15T17:47:15.296Z",
"__v": 0
}
]
const [posts, setPosts] = useState(postsinitial)
return(
<PostContext.Provider value={{posts,setPosts}}>
{props.children}
</PostContext.Provider>
)
}
export default PostState;
Post.js
import React,{useContext} from 'react'
import postContext from "../context/post/postContext"
import CreatePost from './CreatePost';
import Postitem from './Postitem';
const Posts = () => {
const context = useContext(postContext)
const{posts}={context};
return (
<>
<CreatePost/>
<div className="container">
<h2>Your Posts</h2>
{posts.map((post)=>{
return <Postitem key={post._id} post={post}/>
})}
</div>
</>
);
}
// eslint-disable-next-line
export default Posts;
Guys here is the error in Post while destructing, I dont know what is the problem can anybody help please

Related

How to display fetched data in functional react-redux component?

Why I see always "loading..."?
I used redux-toolkit and createSlice and fetch data by axios.
I have not any problem by fetching data and my data is in State.
My problem is displaying fetched data.
My Component code is:
import React, {useEffect, useState} from 'react';
import { useDispatch, useSelector } from 'react-redux';
import {fetchTrackers} from 'dashboard/dashboardSlice';
export default function TrackerManagerDashboard() {
const [trackersList, setTrackersList] = useState(useSelector(state => state.trackersData));
const [activeTracker, setActiveTracker] = useState(useSelector(state => state.activeTracker));
const dispatch = useDispatch();
useEffect(() => {
dispatch(fetchTrackers);
}, []);
if(!trackersList)
return (
<div>loading...</div>
)
return (
<div className="TrackerManagerDashboard">
...
</div>
)
}
and reducer Slice file is:
import { createSlice } from '#reduxjs/toolkit'
import * as env from "../../environments";
import axios from 'axios';
const initialState = {
trackersData: {},
activeTracker: {},
}
const dashboardSlice = createSlice({
name: 'dashboard',
initialState,
reducers: {
setInitialState(state, action) {
state.trackersData = action.payload.data;
state.activeTracker = state.trackersData[Object.keys(state.trackersData)[0]];
},
},
})
export async function fetchTrackers (dispatch, getState) {
try {
const { data } = await axios.get(env.APP_URL + '/fetch/trackers.json');
dispatch(setInitialState({type: 'setInitialState', data }));
} catch(error) {
console.log(error);
}
}
export const { setInitialState} = dashboardSlice.actions
export default dashboardSlice.reducer
Never use useState(useSelector.
That means "create a component-local state with the initial value that the return value of useSelector at the time of first render has". If the Redux state changes later, you will never get to see it, as your useState is already initialized and any change there will not be reflected in your trackersList variable.
Instead, just call useSelector:
const trackersList = useSelector(state => state.trackersData);
const setActiveTracker = useSelector(state => state.activeTracker);

Redux store updating but mapStateToProps not updating component props

When I click on a pointer on my Google Maps component, I can see my store being updated in Redux Devtools but mapStateToProps does not seem to update my component props. Therefore, my Google Maps pointers <InfoWindow> does not open.
If I navigate to another Link(using react-router) from my NavBar and then navigate back to this page, the component receives the updated props from mapStateToProps and the previously clicked Google Maps pointer has the <InfoWindow> open.
I have been trying to debug this for the past 1 week, tried converting components/ClassSchedule/Map/Pure.jsx to a class component but it did not work.
components/ClassSchedule/Map/index.js
import { connect } from 'react-redux';
import { selectClass } from '../../../actions/index';
import Pure from './Pure';
const mapStateToProps = state => ({
selectedClass: state.classMapTable.selectedClass,
});
const mapDispatchToProps = dispatch => ({
selectClass: id => dispatch(selectClass(id)),
});
export default connect(
mapStateToProps,
mapDispatchToProps
)(Pure);
components/ClassSchedule/Map/Pure.jsx
import React from 'react';
import MapContent from './MapContent';
const Map = props => {
return (
<MapContent
googleMapURL={`https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=geometry,drawing,places&key=googleMapsKeyHere`}
loadingElement={<div style={{ height: `100%` }} />}
containerElement={<div style={{ height: `550px` }} />}
mapElement={<div style={{ height: `100%` }} />}
{...props}
/>
);
};
export default Map;
components/ClassSchedule/Map/MapContent.jsx
import React from 'react';
import {
withScriptjs,
withGoogleMap,
GoogleMap,
Marker,
InfoWindow,
} from 'react-google-maps';
import { defaultPosition } from '../../../data/mapData';
import { classes } from '../../../data/classData';
const { zoom, centre } = defaultPosition;
const MapContent = withScriptjs(
withGoogleMap(({ selectedClass, selectClass }) => (
<GoogleMap defaultZoom={zoom} defaultCenter={centre}>
{classes.map(c => (
<Marker
key={`map${c.id}`}
icon={
'https://mt.google.com/vt/icon?psize=30&font=fonts/arialuni_t.ttf&color=ff304C13&name=icons/spotlight/spotlight-waypoint-a.png&ax=43&ay=48&text=%E2%80%A2'
}
position={c.coordinates}
onClick={() => selectClass(c.id)}
>
{selectedClass === c.id && (
<InfoWindow>
<React.Fragment>
<div>{c.area}</div>
<div>{`${c.level} ${c.subject}`}</div>
<div>{`${c.day}, ${c.time}`}</div>
</React.Fragment>
</InfoWindow>
)}
</Marker>
))}
</GoogleMap>
))
);
export default MapContent;
reducers/index.js
import { combineReducers } from 'redux';
import { connectRouter } from 'connected-react-router';
import classMapTable from './classMapTable';
export default history =>
combineReducers({
router: connectRouter(history),
classMapTable,
});
reducers/classMapTable.js
const classMapTable = (state = {}, action) => {
switch (action.type) {
case 'SELECT_CLASS':
return { ...state, selectedClass: action.classId };
default:
return state;
}
};
export default classMapTable;
store/index.js
import { createBrowserHistory } from 'history';
import { createStore, applyMiddleware } from 'redux';
import { routerMiddleware } from 'connected-react-router';
import { composeWithDevTools } from 'redux-devtools-extension';
import createRootReducer from '../reducers';
export const history = createBrowserHistory();
export default function configureStore(preloadedState) {
const store = createStore(
createRootReducer(history),
preloadedState,
composeWithDevTools(applyMiddleware(routerMiddleware(history)))
);
return store;
}
actions/index.js
export const selectClass = classId => ({
type: 'SELECT_CLASS',
classId,
});
After debugging for about 2 weeks, I randomly decided to run npm update. Turns out there wasn't any issue with my code, my npm packages were just outdated/not compatible. I have no idea how I had different versions of react and react-dom. EVERYTHING WORKS NOW.
This was in my package.json:
"react": "^16.7.0",
"react-dev-utils": "^7.0.0",
"react-dom": "^16.4.2",
After updating my package.json:
"react": "^16.8.1",
"react-dev-utils": "^7.0.1",
"react-dom": "^16.8.1",
Moral of the story: KEEP YOUR PACKAGES UP TO DATE.

Can't figure out "Error: Actions must be plain objects. Use custom middleware for async actions."

I'm using React and Redux to build an app to request and subsequently display movie information from an API. In the console, I can get the requested data back but somewhere beyond that I hit the error - "Error: Actions must be plain objects. Use custom middleware for async actions."
Here's my code so far..
Search component:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import { fetchMovie } from '../../actions/index';
class SearchBar extends Component {
constructor(props) {
super(props);
this.state = { term: '' };
this.onInputChange = this.onInputChange.bind(this);
this.onFormSubmit = this.onFormSubmit.bind(this);
}
onInputChange(e) {
this.setState({ term: e.target.value });
}
onFormSubmit(event) {
event.preventDefault();
this.props.fetchMovie(this.state.term);
this.setState({ term: '' });
}
render() {
return (
<div>
<form onSubmit={this.onFormSubmit} className="input-group">
<input
placeholder="Search by movie title, actor, or genre"
className="form-control"
value={this.state.term}
onChange={this.onInputChange}
/>
<span className="input-group-btn">
<button type="submit" className="btn btn-secondary">
Submit
</button>
</span>
</form>
<br />
</div>
);
}
}
function mapDispatchToProps(dispatch) {
return bindActionCreators({ fetchMovie }, dispatch);
}
export default connect(null, mapDispatchToProps)(SearchBar);
Action...
import axios from 'axios';
const API_KEY = '449a384f';
export const FETCH_MOVIE = 'FETCH_MOVIE';
let movies = [];
export function fetchMovie(term) {
const request = axios
.get(`http://www.omdbapi.com/?s=${term}&apikey=${API_KEY}`)
.then(response => {
movies = response.data;
response.json = movies;
})
.catch(error => console.log(error));
return {
type: FETCH_MOVIE,
payload: request,
};
}
Reducer...
import { FETCH_MOVIE } from '../actions/index';
export default function(state = null, action) {
switch (action.type) {
case FETCH_MOVIE:
return [action.payload.data, ...state];
}
return state;
}
CombineReducer...
import { combineReducers } from 'redux';
import MovieReducer from './movie_reducer';
const rootReducer = combineReducers({
movie: MovieReducer,
});
export default rootReducer;
Store...
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
// import ReduxThunk from 'redux-thunk';
import { BrowserRouter, Switch, Route } from 'react-router-dom';
import { createStore, applyMiddleware } from 'redux';
import ReduxPromise from 'redux-promise';
import './index.css';
import App from './App';
import Login from './components/login/login';
import reducers from './reducers/reducer';
import '../node_modules/bootstrap/dist/css/bootstrap.min.css';
import { findDOMNode } from 'react-dom';
import $ from 'jquery';
const createStoreWithMiddleware = applyMiddleware(ReduxPromise)(createStore);
ReactDOM.render(
<Provider store={createStoreWithMiddleware(reducers)}>
<BrowserRouter>
<Switch>
<Route path="/Login" component={Login} />
<Route path="/" component={App} />
</Switch>
</BrowserRouter>
</Provider>,
document.querySelector('#root')
);
Thanks for the help!
Vanilla Redux requires that you return a plain JavaScript object in your action creators. Whenever, you need to perform async operations, you need to introduce middleware like redux-thunk or redux-promise to intercept the returned object an perform additional work so that a plain JavaScript object can ultimately be returned.
You're attempting to use redux-promise, but what you're returning is not causing the middleware to be invoked. Your fetchMovie() method is returning a plain object containing a Promise. In order to use redux-promise, the method needs to return a Promise.
From their documentation:
createAction('FETCH_THING', async id => {
const result = await somePromise;
return result.someValue;
});
Probably, the reason is that you're trying to return promise in the action to reducer.
You're using thunk, so you always can dispatch from action creator
export const fetchMovie = term => dispatch => axios
.get(`http://www.omdbapi.com/?s=${term}&apikey=${API_KEY}`)
.then(response => {
dispatch({
type: FETCH_MOVIE,
payload: response,
});
})
.catch(error => console.log(error));

Redux store error: <Provider> does not support changing `store` on the fly

I am trying to setup my first react/redux/rails app. I am using react_on_rails gem to pass in my current_user and gyms props.
Everything appears to work ok so far except my console shows error:
<Provider> does not support changing `store` on the fly. It is most likely that you see this error because you updated to Redux 2.x and React Redux 2.x which no longer hot reload reducers automatically. See https://github.com/reactjs/react-redux/releases/tag/v2.0.0 for the migration instructions.
Googling gives me hints that this can happen if you try to create a store within a render method, which causes store to get recreated. I don't see that issue here. Where am I going wrong?
//App.js
import React from 'react';
import { Provider } from 'react-redux';
import configureStore from '../store/gymStore';
import Gym from '../components/Gym';
const App = props => (
<Provider store={configureStore(props)}>
<Gym />
</Provider>
);
export default App;
../store/gymStore.jsx
//the store creation.
/*
// my original way
import { createStore } from 'redux';
import gymReducer from '../reducers/';
const configureStore = railsProps => createStore(gymReducer, railsProps);
export default configureStore;
*/
/* possible fix: https://github.com/reactjs/react-redux/releases/tag/v2.0.0 */
/* but adding below does not resolve error */
import { createStore } from 'redux';
import rootReducer from '../reducers/index';
export default function configureStore(railsProps) {
const store = createStore(rootReducer, railsProps);
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept(() => {
const nextRootReducer = require('../reducers').default;
store.replaceReducer(nextRootReducer);
});
}
return store;
}
I am not sure my rendered component is necessary but in case it is:
//compenents/Gym.jsx
import PropTypes from 'prop-types';
import React from 'react';
import { connect } from 'react-redux';
import LeftMenu from './LeftMenu';
class Gym extends React.Component {
static propTypes = {
//name: PropTypes.string.isRequired // this is passed from the Rails view
};
/**
* #param props - Comes from your rails view.
*/
constructor(props) {
super(props);
this.state = {
current_user: this.props.current_user,
gyms: JSON.parse(this.props.gyms),
active_gym: 1, //JSON.parse(this.props.gyms)[0],
name: 'sean',
title: 'Gym Overview'
};
}
updateName = name => {
this.setState({ name });
};
isLoggedIn = () => {
if (this.state.current_user.id != '0') {
return <span className="text-success"> Logged In!</span>;
} else {
return <span className="text-danger"> Must Log In</span>;
}
};
isActive = id => {
if (this.state.active_gym == id) {
return 'text-success';
}
};
render() {
return (
<div className="content">
<h2 className="content-header">{this.state.title}</h2>
{LeftMenu()}
{this.state.current_user.id != '0' ? <span>Welcome </span> : ''}
{this.state.current_user.first_name}
<h3 className="content-header">Your Gyms</h3>
<ul>
{this.state.gyms.map((gym, key) => (
<li key={key} className={this.isActive(gym.id)}>
{gym.name}
</li>
))}
</ul>
{this.isLoggedIn()}
<hr />
{/*
<form>
<label htmlFor="name">Say hello to:</label>
<input
id="name"
type="text"
value={this.state.name}
onChange={e => this.updateName(e.target.value)}
/>
</form>
*/}
</div>
);
}
}
function mapStateToProps(state) {
return {
current_user: state.current_user,
gyms: state.gyms,
active_gym: state.active_gym
};
}
export default connect(mapStateToProps)(Gym);

Using redux-connected component as screen in StackNavigator

I'm creating an react native app using create-react-native-app, react-navigation and react-redux. I'm trying to add a redux-connected component as a screen into a nested StackNavigator (though the nesting seems to not make a difference, it doesn't work either way) and consistently am getting an error message saying Route 'MilkStash' should declare a screen. When I remove the redux connection from the MilkStash.js file, everything works fine. Any idea how to get this working?
App.js
import React, { Component } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './src/reducers';
import AppWithNavigation from './src/AppWithNavigation';
export default () => (
<Provider store = {createStore(rootReducer)}>
<AppWithNavigation />
</Provider>
);
AppWithNavigation.js
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { StyleSheet, Text, View, Image, Button } from 'react-native';
import { DrawerNavigator, StackNavigator } from 'react-navigation';
import MilkStash from './screens'
import { StatsScreen, FAQScreen, AddMilk, AccountScreen } from './screens';
export default class AppWithNavigation extends React.Component {
render() {
return (
<MenuNavigator />
);
}
}
const MilkNavigator = StackNavigator(
{ Milk: { screen: MilkStash},
AddMilk: { screen: AddMilk }
},
);
const AccountNavigator = StackNavigator(
{ Account: {screen: AccountScreen}}
);
const StatsNavigator = StackNavigator(
{ Stats: {screen: StatsScreen }}
);
const FAQNavigator = StackNavigator(
{ FAQ: {screen: FAQScreen}}
)
const MenuNavigator = DrawerNavigator({
Milk: { screen: MilkNavigator},
Account: {screen: AccountNavigator},
Stats: {screen: StatsNavigator},
FAQ: {screen: FAQNavigator},
}
);
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
backgroundColor: '#ecf0f1',
}
});
MilkStash.js
import React, {Component} from 'react';
import { StyleSheet, Text, View} from 'react-native';
import { StackNavigator } from 'react-navigation';
import { connect } from 'react-redux';
import { Milk } from '../../core/models/milk';
import styles from './styles.js';
export class MilkStash extends Component {
constructor(props){
super(props);
}
render() {
return (
<View style={styles.container}>
....displaying data goes here
</View>
)
}
}
function mapStateToProps(state){
return{
milkStash: state.milkStash
}
}
function mapDispatchToProps(dispatch){
return {
addMilk: (milk) => dispatch(addMilk(milk)),
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MilkStash);
milk-reducer.js
import {ADD_MILK} from '../constants';
const milkReducer = (state = {milkStash: []}, action = {}) => {
switch(action.type){
case ADD_MILK:
var item = action.payload;
return state
.update('milkStash', (milkStash) =>
{
var milkStashCopy = JSON.parse(JSON.stringify(milkStash));
milkStashCopy.concat(item);
return milkStashCopy;
});
default:
return state;
}
}
export default milkReducer;
reducers.js
export * from './milk.js';
import milkReducer from './milk';
import { combineReducers } from 'redux';
export default rootReducer = combineReducers({
milk: milkReducer
});
I figured out the answer and thought I would help prevent someone else struggling with this for 3 days. The issue had to do with the way I was importing the exports from MilkStash.js. Apparently using import MilkStash from './screens' will cause the error but changing it to import MilkStashContainer from './screens/MilkStash/MilkStash.js will fix the problem.

Resources