I am trying to run the code from the tutorial in
https://codepen.io/stowball/post/a-dummy-s-guide-to-redux-and-thunk-in-react#understanding-redux-4
on
https://jscomplete.com/repl/
, but the execution is failing with the following error:
ReferenceError: connect is not defined
I tried using an import statement at top of the file :
import { connect } from 'react-redux';
But it did not help resolve the error.
Is there something wrong in my understanding of the way jscomplete works?Any explanation would help
TIA
Update : Pasting the code as requested :
import { connect } from 'react-redux';
class ItemList extends React.Component {
componentDidMount() {
this.props.fetchData('https://5826ed963900d612000138bd.mockapi.io/items');
}
render() {
if (this.props.hasErrored) {
return <p>Sorry! There was an error loading the items</p>;
}
if (this.props.isLoading) {
return <p>Loading…</p>;
}
return (
<ul>
{this.props.items.map((item) => (
<li key={item.id}>
{item.label}
</li>
))}
</ul>
);
}
}
const mapStateToProps = (state) => {
return {
items: state.items,
hasErrored: state.itemsHasErrored,
isLoading: state.itemsIsLoading
};
};
const mapDispatchToProps = (dispatch) => {
return {
fetchData: (url) => dispatch(itemsFetchData(url))
};
};
connect(mapStateToProps, mapDispatchToProps)(ItemList)
function itemsHasErrored(bool) {
return {
type: 'ITEMS_HAS_ERRORED',
hasErrored: bool
};
}
function itemsIsLoading(bool) {
return {
type: 'ITEMS_IS_LOADING',
isLoading: bool
};
}
function itemsFetchDataSuccess(items) {
return {
type: 'ITEMS_FETCH_DATA_SUCCESS',
items
};
}
function itemsFetchData(url) {
return (dispatch) => {
dispatch(itemsIsLoading(true));
fetch(url)
.then((response) => {
if (!response.ok) {
throw Error(response.statusText);
}
dispatch(itemsIsLoading(false));
return response;
})
.then((response) => response.json())
.then((items) => dispatch(itemsFetchDataSuccess(items)))
.catch(() => dispatch(itemsHasErrored(true)));
};
}
function itemsHasErrored(state = false, action) {
switch (action.type) {
case 'ITEMS_HAS_ERRORED':
return action.hasErrored;
default:
return state;
}
}
function itemsIsLoading(state = false, action) {
switch (action.type) {
case 'ITEMS_IS_LOADING':
return action.isLoading;
default:
return state;
}
}
function items(state = [], action) {
switch (action.type) {
case 'ITEMS_FETCH_DATA_SUCCESS':
return action.items;
default:
return state;
}
}
combineReducers({
items,
itemsHasErrored,
itemsIsLoading
});
function configureStore(initialState) {
return createStore(
rootReducer,
initialState,
applyMiddleware(thunk)
);
}
const store = configureStore(); // You can also pass in an initialState here
render(
<Provider store={store}>
<ItemList />
</Provider>,
document.getElementById('app')
);
ReactDOM.render(<ItemList/>,mountNode)
this is due to a few issues. First, you need to setup Babel transpiler and its configuration for react in order to process ES6 import statements.
Seems like jscomplete.com (and jsFiddle) does not completely support this.
There are a few issues in the code such as referring to undeclared variables (eg: mountNode) but the main issue is that jsComplete.com not understanding the import statements.
You will have better luck running this code in a computer with VS Code or a similar editor.
Look in to create-react-app which solves all of these development setup things for you when developing react applications.
Related
I'm learning Redux-Saga and everything works well with { configureStore, getDefaultMiddleware, createAction, createReducer }. However, I cannot successfully implement createSlice.
My actions seem to be dispatched just fine (though I'm not sure since I have multiple Redux stores and placing console.log inside createSlice doesn't seem to work...). I just cannot get the store values - after dispatched action the relevant state value (initially '') becomes undefined. I did wrap my component inside Provider and all. Can someone enlighten me how does createSlice work? Thanks.
RESOLVED I had a bug somewhere else in my code, that's why the reducers weren't working proberly. BUT what I was asking about and what was causing my problems is this: actions passed to createSlice must be 'pure' functions, meaning: (state, action) -> state, nothing fancy. That's why I had to remove my fetching functions (getData1 and getData2) from this createSlice.
ComponentWrapper returns this
<Provider store={toolkitCreateSliceStore}>
<ReduxToolkitCreateSliceComponent />
</Provider>
Component (Buttons just dispatch actions)
class ReduxToolkitCreateSliceComponent extends React.Component {
render () {
return (
<>
<h2>
{this.props.data1}
{(this.props.data1!=='' && this.props.data2!=='') ? ', ' : ''}
{this.props.data2}
</h2><br/>
<h3>{this.props.message}</h3>
<Button1 />
<Button2 />
<Button3 />
</>
);
}
}
function mapStateToProps(state) {
return {
data1: state.toolkitCreateSliceReducer.data1,
data2: state.toolkitCreateSliceReducer.data2,
message: state.toolkitCreateSliceReducer.message
};
}
export default connect(mapStateToProps)(ReduxToolkitCreateSliceComponent);
Redux Toolkit slice
import { createSlice } from "#reduxjs/toolkit";
import axios from "axios";
const initialSliceState = {
data1: '',
data2: '',
message: ''
};
const slice = createSlice({
name: "slice",
initialState: initialSliceState,
reducers: {
getData1: (state, action) => {
return dispatch => {
dispatch(loading1());
return axios.get('http://localhost:8081/data1')
.then(function (response) {
if (response.status === 200) {
dispatch(setResponse1(response.data));
}
}).catch(error => dispatch(displayError1(error)));
};
},
getData2: (state, action) => {
return dispatch => {
dispatch(loading2());
return axios.get('http://localhost:8081/data2')
.then(function (response) {
if (response.status === 200) {
dispatch(setResponse2(response.data));
}
}).catch(error => dispatch(displayError2(error)));
};
},
setResponse1: (state, action) => {
state.data1 = action.payload;
state.message = 'success';
},
setResponse2: (state, action) => {
state.data2 = action.payload;
state.message = 'success';
},
reset: (state, action) => {
state.data1 = '';
state.data2 = '';
state.message = 'reset';
},
loading1: (state, action) => {
state.message = 'loading';
},
loading2: (state, action) => {
state.message = 'loading';
},
displayError1: (state, action) => {
state.message = action.payload;;
},
displayError2: (state, action) => {
state.message = action.payload;;
}
}
});
export const toolkitCreateSliceReducer = slice.reducer;
const { getData1, getData2, setResponse1, setResponse2, reset, loading1, loading2,
displayError1, displayError2} = slice.actions;
export default slice;
Redux Toolkit store
const middleware = [
...getDefaultMiddleware()
];
const toolkitCreateSliceStore = configureStore({
reducer: {
toolkitCreateSliceReducer
},
middleware
});
export default toolkitCreateSliceStore;
Your "reducers" are very wrong.
A reducer must never have any side effects like AJAX calls.
You've written some Redux "thunk" functions where your reducers should be:
getData1: (state, action) => {
return dispatch => {
dispatch(loading1());
return axios.get('http://localhost:8081/data1')
.then(function (response) {
if (response.status === 200) {
dispatch(setResponse1(response.data));
}
}).catch(error => dispatch(displayError1(error)));
};
},
This is a thunk, not a reducer.
A reducer would be something like:
getData(state, action) {
return action.payload;
}
I'd specifically recommend reading through our brand-new "Redux Essentials" core docs tutorial, which teaches beginners "how to use Redux, the right way", using our latest recommended tools and practices like Redux Toolkit. It specifically covers how reducers should work, how to write reducers with createSlice, and how to write and use thunks alongside createSlice:
https://redux.js.org/tutorials/essentials/part-1-overview-concepts
If you're interested in creating async actions, let me recommend you an npm package that I created and use. It is saga-toolkit that allows async functions to get resolved by sagas.
slice.js
import { createSlice } from '#reduxjs/toolkit'
import { createSagaAction } from 'saga-toolkit'
const name = 'example'
const initialState = {
result: null,
loading: false,
error: null,
}
export const fetchThings = createSagaAction(`${name}/fetchThings`)
const slice = createSlice({
name,
initialState,
extraReducers: {
[fetchThings.pending]: () => ({
loading: true,
}),
[fetchThings.fulfilled]: ({ payload }) => ({
result: payload,
loading: false,
}),
[fetchThings.rejected]: ({ error }) => ({
error,
loading: false,
}),
},
})
export default slice.reducer
sagas.js
import { call } from 'redux-saga/effects'
import { takeLatestAsync } from 'saga-toolkit'
import API from 'hyper-super-api'
import * as actions from './slice'
function* fetchThings() {
const result = yield call(() => API.get('/things'))
return result
}
export default [
takeLatestAsync(actions.fetchThings.type, fetchThings),
]
Ive check if both the action and the reducer are getting called. They both get called but the mapstatetoprops isnt changing the state. Also means that action.tpye is getting change. I dont understand why it wont work and any help is appreciated.
This is my reducer
const calling = (state={}, action) => {
switch (action.type) {
case 'CALLING_USER':
return {type: action.type, callLoading: true, isCalling: true, friend: action.friendSocket}
case 'ANSWERING_CALL':
console.log("ANSWER CALL")
return {type: action.type, callLoading: true, isCalling: true, friend: action.data.socket, offer: action.data.offer}
case 'CALL_SUCCESS':
return {
isCalling: true,
callLoading: false
}
case 'CALL_ERROR':
return {
callLoading: false,
error: true,
callErrorText: action.err,
}
default:
return {isCalling: false}
}
}
export default calling
Action
export const CALLING_USER = 'CALLING_USER'
function callingUser(friendSocket) {
return {
type: CALLING_USER,
friendSocket
}
}
export const ANSWERING_CALL = 'ANSWERING_CALL'
function answeringCall(data) {
return {
type: ANSWERING_CALL,
data
}
}
export const CALL_ERROR = 'CALL_ERROR'
function callError(err) {
return {
type: CALL_ERROR,
err
}
}
export function callSetup(friendSocket, dispatch) {
console.log("IS THIS WORKING")
console.log(dispatch)
return function(dispatch) {
console.log(dispatch)
dispatch(callingUser(friendSocket))
}
}
export function answerSetup(data) {
console.log("ANSWER SETUP")
return function(dispatch) {
dispatch(answeringCall(data))
}
}
It gets called here
import { answerSetup } from './Redux/Actions'
import store from './Redux/store'
import io from "socket.io-client";
export const startGlobalSockets = (mySocket) => {
mySocket.on("gettingCalled", data => {
console.log("GETTING CALLED SOCKET", data)
store.dispatch(answerSetup(data))
})
}
export default startGlobalSockets
It should make this statement true.
const mapStateToProps = (state) => ({
user: state.session.user,
calling: state.calling
})
const isVideo = (this.props.calling.isCalling ?
<Grid style={{width:"100%", height: "100%"}} item sm={10}>
<VideoScreen/>
</Grid> :
<Grid style={{height: '57em', overflow: 'auto'}} item sm={10}>
<div className="parent">
<Post/>
</div>
</Grid>)
...
export default withRouter(connect(mapStateToProps)(Home))
I assume you did not use connect as required. You are importing it but I don’t see where you use it. It would help if you’d post the whole code snippet including the use of connect and mapStateToProps and mapDispatchToProps.
In your example answerSetup returns a function which internally calls answeringCall instead of object with action type and payload (like answeringCall). Unless you have some middlewares like react-thunk which handle functions and you have special logic there, I don't see why not using answeringCall directly in your calling component.
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)
without redux it works so that not a api connection problem
I have an express app connected to react with proxy I have already managed to display my data in react but now i want to make that in redux soo:
There is my problem, i have maked all the reducers/action, store and combine reducer but I didn't see any datas in my page and i haven't any errors
There is my code :
Action
export const api = ext => `http://localhost:8080/${ext}`;
//
// ─── ACTION TYPES ───────────────────────────────────────────────────────────────
//
export const GET_ADVERTS = "GET_ADVERTS";
export const GET_ADVERT = "GET_ADVERT";
//
// ─── ACTION CREATORS ────────────────────────────────────────────────────────────
//
export function getAdverts() {
return dispatch => {
fetch("adverts")
.then(res => res.json())
.then(payload => {
dispatch({ type: GET_ADVERTS, payload });
});
};
}
export function getAdvert(id) {
return dispatch => {
fetch(`adverts/${id}`)
.then(res => res.json())
.then(payload => {
dispatch({ type: GET_ADVERT, payload });
});
};
}
reducer
import { combineReducers } from "redux";
import { GET_ADVERTS, GET_ADVERT } from "../actions/actions";
const INITIAL_STATE = {
adverts: [],
advert: {}
};
function todos(state = INITIAL_STATE, action) {
switch (action.type) {
case GET_ADVERTS:
return { ...state, adverts: action.payload };
case GET_ADVERT:
return { advert: action.payload };
default:
return state;
}
}
const todoApp = combineReducers({
todos
});
export default todoApp;
index.js
//imports
const store = createStore(todoApp, applyMiddleware(thunk));
ReactDOM.render(
<Provider store={store}>
<BrowserRouter>
<App />
</BrowserRouter>
</Provider>,
document.getElementById("app")
);
My advertlist page :
//imports..
class Adverts extends Component {
componentDidMount() {
this.props.getAdverts();
}
render() {
const { adverts = [] } = this.props;
return (
<div>
<Header />
<h1>Adverts</h1>
{adverts.map(advert => (
<li key={advert._id}>
<a href={"adverts/" + advert._id}>
{advert.name} {advert.surname}
</a>
</li>
))}
<Footer />
</div>
);
}
}
const mapStateToProps = state => ({
adverts: state.adverts
});
export default connect(
mapStateToProps,
{ getAdverts }
)(Adverts);
I think your problem is here:
function mapStateToProps(state) {
return {
**adverts: state.adverts**
};
}
It should work if you change state.adverts to state.todos.adverts:
function mapStateToProps(state) {
return {
adverts: state.todos.adverts
};
}
Because your reducer is called todos, and it has state { adverts }, that's why you cannot access adverts even tho they are obtained.
You can check out working version here: https://codesandbox.io/s/olqxm4mkpq
The problem is, when you just create a store with one reducer without using combine reducer, it is possible to refer it directly in the ContainerS, like this:
const mapStateToProps = state => {
return{
*name of var*: state.adverts /*direct refers to adverts*/
}
}
But, when it use combined-reducer , it has to refer to an exact reducer that you want to use.like this :
const mapStateToProps = state => {
return{
*name of var* : state.todos.adverts (indirect refers to adverts from combined-reducer todos)
}
}
I am implementing a project where the data going to be shared in different components. So I decided to use redux-react for state management.
I used redux react async api call to get data from api. However I got undefined when the component mounted for the first time and returned actual data.
However, when I tried to implement some function on returned data, I got this error:
"Cannot read property of undefined"
I can see the state in redux developer tools and it has data and the logs function display action correctly. I can not understand why I am getting undefined. Here is my code:
const initialState = {
candidate: {},
companies: [],
offers: [],
moreStatehere:...
}
Reducer for the candidate
export default function profileReducer(state = initialState, action) {
switch(action.type) {
case FETCH_POSTS_FAILURE:
return Object.assign({}, state, {
didInvalidate: true
})
case REQUEST_PROFILE:
return Object.assign({}, state, {
isFetching: true,
didInvalidate: false
})
case RECEIVE_PROFILE:
return {
...state,
candidate: action.data
}
default:
return state;
}
}
root reducer
const rootReducer = combineReducers({
profiles: profileReducer
})
export default rootReducer;
create store
const composeEnhanser = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__||compose;
const loggerMiddleware = createLogger()
export default function configureStore() {
return createStore(
rootReducer,
composeEnhanser(applyMiddleware(thunkMiddleware,
loggerMiddleware))
);
}
index.js
const store = configureStore();
const app = (
<Provider store= {store}>
<BrowserRouter>
<App/>
</BrowserRouter>
</Provider>
)
ReactDOM.render(app, document.getElementById('root'));
registerServiceWorker();
action creator/api call
export function feachProfiles() {
return function (dispatch) {
dispatch(requestProfile)
return fetch(API_URL)
.then(
response => response.json(),
error => console.log('An error occurred.', error)
)
.then(json =>
dispatch(receiveProfile(json))
)
}
}
componentuse
class CandidatesList extends Component {
constructor (props){
super (props)
}
componentWillMount() {
this.props.feachProfiles();
}
handleClick() {
}
componentWillUnmount() {
}
render() {
const candidate = this.props.profiles.map(profile=>(
<div> </div>
));
return (
<div>
<ViewCandidate
/>
</div>
);
}
}
const mapStateToProps = state => {
return {
profiles: state.profiles.candidate || []
}
}
const mapDispatchToProps = (dispatch) => {
return {
feachProfiles: bindActionCreators(feachProfiles, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(CandidatesList);
action RECEIVE_PROFILE #
redux-logger.js:1 prev state {profiles: {…}}
redux-logger.js:1 action {type: "RECEIVE_PROFILE", data: {…}}
redux-logger.js:1 next state {profiles: {…}}
make sure to write this just before map function
if (this.props.profiles.length === 0) return null;
this.props.profiles should have array length of greater than 0
const candidate = this.props.profiles.map(profile=>(
<div> </div>
));