I'm trying setup a Redux + React Router app. I think the problem is with ImmutableJS, but I do not understand how to resolve it.
client.js
import { fromJS } from 'immutable'
import React from 'react'
import { render, unmountComponentAtNode } from 'react-dom'
import { AppContainer } from 'react-hot-loader'
import { Provider } from 'react-redux'
import { match, Router, browserHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
import { createStore, combineReducers, compose, applyMiddleware } from 'redux'
import { routerReducer, routerMiddleware } from 'react-router-redux'
function createSelectLocationState(reducerName) {
let prevRoutingState;
let prevRoutingStateJS;
return (state) => {
const routingState = state.get(reducerName); // or state.routing
if (!routingState.equals(prevRoutingState)) {
prevRoutingState = routingState;
prevRoutingStateJS = routingState.toJS();
}
return prevRoutingStateJS;
};
}
function configureStore(history, initialState) {
const reducer = combineReducers({
routing: routerReducer
});
return createStore(
reducer,
initialState,
compose(
applyMiddleware(
routerMiddleware(history)
)
)
);
}
const initialState = fromJS(window.__INITIAL_STATE__);
const store = configureStore(browserHistory, initialState);
const history = syncHistoryWithStore(browserHistory, store, {
selectLocationState: createSelectLocationState('routing')
});
const rootNode = document.getElementById('root');
const renderApp = () => {
const routes = require('./routes');
match({ history, routes }, (error, redirectLocation, renderProps) => {
render(
<AppContainer>
<Provider store={store}>
<Router {...renderProps} />
</Provider>
</AppContainer>,
rootNode
);
});
};
// Enable hot reload by react-hot-loader
if (module.hot) {
const reRenderApp = () => {
try {
renderApp();
} catch (error) {
const RedBox = require('redbox-react').default;
render(<RedBox error={error} />, rootNode);
}
};
module.hot.accept('./routes', () => {
setImmediate(() => {
// Preventing the hot reloading error from react-router
unmountComponentAtNode(rootNode);
reRenderApp();
});
});
}
renderApp();
I get this error:
Uncaught TypeError: state.get is not a function
In state this object
I use "react": "^15.3.2", "redux": "^3.6.0", "react-router": "^3.0.0"
UPDATE 1
I now use combineReducers from redux-immutable:
import {combineReducers} from 'redux-immutable'
But get an error:
Uncaught TypeError: routingState.equals is not a function
Here:
UPDATE 2
I fixed hereinabove issue, but there one more error
All code i posted in this repository
The problem stays at src/index.js file with the require statement of route.js.
When you require es6 module which has default, you have to use the default from required module. Something like,
const routes = require('./routes').default;
This fixed your issues without any other change on your git repo.
The combineReducers you are using is not using immutable. Each branch is immutable by setting fromJS(blah) but not on the highest level of your state. Use redux-immutable instead:
import {combineReducers} from 'redux-immutable';
Related
Lately, I have been trying to use Redux but I get no error and no dev tool error and my page are blank.
So I started my code with the basic Redux boilerplate. I created a userslice, a store and then I provided the store as a wrapper for the <app/>.
Yet after spending hours I can't get to fix the code. Code should just give me back the username inside a div using useselector hook that initialized but it does not seem to work.
App.js:
import React from 'react';
import { useSelector } from 'react-redux';
import './App.css';
function App() {
const username = useSelector(state => state.username)
return (
<div className="App">
{username}
</div>
);
}
export default App;
userSlice.js
import { createSlice } from "#reduxjs/toolkit";
export const userSlice = createSlice({
name:'user'
,
initialState:{
username:'Tony stark',
post:'',
},
reducers:{
updatePost:(state,action)=>{
state.username = action.payload;
}
}})
export const { updatePost} = userSlice.actions;
export default userSlice.reducers;
store.js
import { configureStore } from '#reduxjs/toolkit';
import userSlice from '../redux/userSlice'
export const store = configureStore({
reducer: {
user: userSlice,
},
});
index.js
import React from 'react';
import { createRoot } from 'react-dom/client';
import { Provider } from 'react-redux';
import { store } from '../src/redux/store'
import App from './App';
import './index.css';
const container = document.getElementById('root');
const root = createRoot(container);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
As it's in the userSlice you'll need to get it from the user property of the Redux root state, like so:
const username = useSelector(state => state.user.username)
Your initial Redux state (annotated) should look like this:
{ // <-- state
user: { // <-- state.user
username: 'Tony stark', // <-- state.user.username
post: '' // <-- state.user.post
}
}
I am trying to use AppLoading on Expo to preload data from firebase, before the app goes to the homepage. I keep receiving an error.
"Error: could not find react-redux context value; please ensure the component is wrapped in a
< Provider > "
import React, { useState } from "react";
import { createStore, combineReducers, applyMiddleware } from "redux";
import { Provider } from "react-redux";
import ReduxThunk from "redux-thunk";
import productsReducer from "./store/productReducer";
import createdProducts from "./store/createdProductReducer";
import storeName from "./store/StoreNameReducer";
import authReducer from "./store/authReducer";
import { useDispatch } from "react-redux";
import * as ProdActions from "./store/productActions";
import AppLoading from "expo-app-loading";
import InventoryNavigator from "./navigation/InventoryNavigator";
const rootReducer = combineReducers({
products: productsReducer,
availableProducts: createdProducts,
auth: authReducer,
storeName: storeName,
});
const store = createStore(rootReducer, applyMiddleware(ReduxThunk));
export default function App() {
const [fireBLoaded, setFireBLoaded] = useState(false);
const dispatch = useDispatch();
const fetchFirebase = () => {
dispatch(ProdActions.fetchAvailableProducts());
dispatch(ProdActions.fetchStoreName());
dispatch(ProdActions.fetchProducts());
};
if (!fireBLoaded) {
return (
<AppLoading
startAsync={fetchFirebase}
onFinish={() => setFireBLoaded(true)}
onError={console.warn}
/>
);
} else {
return (
<Provider store={store}>
<InventoryNavigator />
</Provider>
);
}
}
what I have tried:
const fetchFirebase = async () => {
any help would be greatly appreciated, I am still new to React Native.
The error tells that there is no Redux.Provider when fetching from Firebase.
To fix it, you should also wrap you <AppLoading ... /> into that <Provider store={store}> ....
It should look like following:
<Provider store={store}>
<AppLoading ... />
<Provider/>
Your fetchFirebase function should be async
Like this -
const fetchFirebase = async () => {
// Perform Aysnc operations here...
dispatch(ProdActions.fetchAvailableProducts());
dispatch(ProdActions.fetchStoreName());
dispatch(ProdActions.fetchProducts());
};
I don't see any other errors here Other than this one
i want to cnnect redux-saga witdh react-native but this error keep happen...
TypeError: store.getState is not a function. (In 'store.getState()',
'store.getState' is undefined
Warning: Failed prop type: Invalid prop store of type function
supplied to Provider, expected object
this is my code
(index.js)
import {AppRegistry} from 'react-native';
import Root from './App';
import {name as appName} from './app.json';
AppRegistry.registerComponent(appName, () => Root);
(App.js)
import React from 'react';
import Store from './store/configureStore'
import {Provider} from 'react-redux';
import {App} from './src/index';
const Root = () => {
return (
<Provider store={Store}>
<App />
</Provider>
);
};
export default Root;
(src/index.js)
import React from 'react';
import Navigator from './Screens/Navigator';
import styled from 'styled-components/native';
const App = ({}) => {
return (
<Navigator/>
);
};
export {App};
(store/cofigurestore.js)
import { applyMiddleware, createStore, compose } from 'redux';
import createSagaMiddleware from 'redux-saga';
import { composeWithDevTools } from 'redux-devtools-extension';
import reducer from '../reducers';
import rootSaga from '../sagas';
const Store = () => {
const sagaMiddleware = createSagaMiddleware();
const middlewares = [sagaMiddleware];
const enhancer = process.env.NODE_ENV === 'production'
? compose(applyMiddleware(...middlewares))
: composeWithDevTools(
applyMiddleware(...middlewares),
);
const store = createStore(reducer, enhancer);
store.sagaTask = sagaMiddleware.run(rootSaga);
return store;
};
export default Store;
(reducer/index.js)
import { combineReducers } from 'redux';
import user from './user';
import post from './post';
// (이전상태, 액션) => 다음상태
const rootReducer = (state, action) => {
switch (action.type) {
// case HYDRATE:
// // console.log('HYDRATE', action);
// return action.payload;
default: {
const combinedReducer = combineReducers({
user,
post,
});
return combinedReducer(state, action);
}
}
};
export default rootReducer;
(sage/index.js)
import { all, fork } from 'redux-saga/effects';
import axios from 'axios';
import postSaga from './post';
import userSaga from './user';
export default function* rootSaga() {
yield all([
fork(postSaga),
fork(userSaga),
]);
}
please help me ......... i want to resolve this problem...... but i don't know how can i do that
In you App.js you should be passing the result of calling you Store function to the Provider and not the function itself.
const Root = () => {
return (
<Provider store={Store()}>
<App />
</Provider>
);
};
I am new to the saga world. Although I have worked with thunk on react-native territory, I am very confused at the moment. I am trying to get the skeleton of my project going which I expect to get very large soon. With that in mind, I am trying to separate the logic into multiple files.
I have gotten the reducer to fire except it is not the way I want. I am not sure how it is even happening. My saga does not fire but my state updates. I see the console log from my reducer but nothing from the saga watcher function. What should I change?
Index.js
import React from 'react'
import { render } from 'react-dom'
import { createStore, applyMiddleware } from 'redux'
import createSagaMiddleware from 'redux-saga'
import { Provider } from 'react-redux'
import reducer from './reducers'
import rootSaga from './sagas'
import App from './App'
const sagaMiddleware = createSagaMiddleware()
const store = createStore(
reducer,
applyMiddleware(sagaMiddleware)
)
sagaMiddleware.run(rootSaga)
render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById("etlRootDiv"),
);
App.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import FileExtensionSelector from './components/FileExtensionSelector'
import { setFileExtension } from './actions'
class App extends Component {
constructor(props) {
super(props)
}
handleTypeSelect() {
console.log('handle more click');
this.props.setFileExtension('zip');
console.log(this.props);
}
componentWillReceiveProps(nextProps){
console.log(nextProps);
}
render() {
return (
<div>
<FileExtensionSelector onFileTypeSelect={this.handleTypeSelect.bind(this)} />
<div>{this.props.fileType} ...asdasd</div>
</div>
)
}
}
const mapStateToProps = ({ metaState }) => {
const { fileType } = metaState;
return { fileType };
};
const mapDispatchToProps = (dispatch) => ({
setFileExtension(ext) {
dispatch(setFileExtension(ext))
}
})
export default connect(mapStateToProps, mapDispatchToProps)(App)
reducers/index.js
import { combineReducers } from 'redux';
import metaState from './MetaStateReducer';
const rootReducer = combineReducers({
metaState,
})
export default rootReducer
reducers/metastatereducer.js
const INITIAL_STATE = {
fileType: null,
hasHeader: false,
};
export default function (state = INITIAL_STATE, action) {
switch (action.type) {
case 'SET_FILE_EXTENSION':
console.log('/// in set file reducer ///');
console.log(action);
// console.log({ ...state, ...INITIAL_STATE, fileType: action.payload });
return { ...state,...INITIAL_STATE, fileType: action.payload };
default:
return state;
}
}
actions/metaStateActions.js
function action(type, payload = {}) {
return { type, ...payload }
}
export const SET_FILE_EXTENSION = "SET_FILE_EXTENSION";
export const setFileExtension = (extension) => action( SET_FILE_EXTENSION, { payload: extension });
actions/index.js
export { setFileExtension, SET_FILE_EXTENSION } from './metaDataActions';
sagas/metastatesagas.js
import { take, put } from 'redux-saga/effects'
import { SET_FILE_EXTENSION } from '../actions';
function* watchFileExtension(ext) {
console.log(' --- in watch file ext ---');
const { extension } = yield take(SET_FILE_EXTENSION)
console.log(`set extension is ${extension}`);
// yield put({ type: 'SET_FILE_EXTENSION', payload: ext });
}
export const metaStateSagas = [
take("SET_FILE_EXTENSION", watchFileExtension),
]
sagas/index
import { all } from 'redux-saga/effects'
import { metaStateSagas } from './MetaStateSagas';
export default function* rootSaga() {
yield all([
...metaStateSagas,
])
}
redux-saga always passes an action along to the store before attempting to process itself. So, the reducers will always run before any saga behavior executes.
I think the error is that your metaStateSagas array needs to use takeEvery, not take, but I'm not entirely sure. Try that and see if it fixes things.
I'm using Redux for a React project. For some reason, my reducer doesn't recognise the action type sent to it or even the action itself. I get this error TypeError: Cannot read property 'type' of undefined. And I know I'm using dispatch.
The api from the server works fine, I've tested through Postman.
But I don't understand what's happening with redux.
Please, can someone advise me?
Before stamping down my question, I've read many SO posts that looks similar but none has answered my question, hence why I'm asking it here.
Thanks.
Action:
import axios from 'axios';
export const GET_ALL_USERS = 'GET_ALL_USERS';
export const showAllUsers = () => dispatch => {
console.log('USERS ACTION');
return axios
.get('/api/users/all')
.then(res => {
console.log('GETTING ALL USERS ACTION', res);
return dispatch({
type: GET_ALL_USERS,
payload: res.data
});
})
.catch(err => console.log('Oops! Cannot get any users.'));
};
Reducer:
import { GET_ALL_USERS } from '../actions/usersActions';
const InitialState = {
users: null,
loading: false
};
export default function(state = InitialState, action) {
console.log('USERS REDUCER', action);
switch (action.type) {
case GET_ALL_USERS:
return {
...state,
users: action.payload
};
default:
return state;
}
}
React:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore, applyMiddleware, compose } from 'redux';
import thunk from 'redux-thunk';
import './index.css';
import App from './containers/App';
import registerServiceWorker from './registerServiceWorker';
import rootReducer from './reducers';
let middleware = [thunk];
const store = createStore(
rootReducer,
{},
compose(
applyMiddleware(...middleware),
window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
)
);
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
React component:
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { showAllUsers } from '../../actions/usersActions';
export class Admin extends Component {
constructor(props) {
super(props);
this.state = {};
}
componentDidMount() {
this.props.showAllUsers();
}
render() {
const { users } = this.props;
console.log(`All users in admin`, this.props.users);
return (
<div className="admin">
<h1 className="admin__title">Admin Board</h1>
{this.props.users.map(user => {
return (
<article>
<img src={user.avatar} alt={user.username} />
<p>{user.firstName}</p>
<p>{user.lastName}</p>
<p>{user.username}</p>
<p>{user.email}</p>
</article>
);
})}
</div>
);
}
}
const mapStateToProps = state => ({
users: state.users
});
export default connect(mapStateToProps, { showAllUsers })(Admin);
showAllUsers is async action. You need some middleware to dispatch async action.
Use redux-thunk or redux-saga and integrate it with store.
It will help to perform asynchronous dispatch
import thunkMiddleware from 'redux-thunk';
let middleWares = [thunkMiddleware];
const store = createStore(
rootReducer, applyMiddleware(...middleWares)
)
If you dispatch async action in synchronous way, the error TypeError: Cannot read property type of undefined will be thrown.