So I am learning how to use redux and redux persist and currently I stuck at a problem right now. Whenever I try to map my store's content, it throws an error saying TypeError: tasks.map is not a function and I don't really know why the problem is occuring. I've googled around and tried debugging it, also tried to re-write the code a couple of time but it was all to no avail. Here's my entire code:
rootReducer.js
import React from 'react'
let nextToDo = 0;
const task=[
{
}
]
const reducer =(state=task, action)=>
{
switch(action.type)
{
case 'add':
return[
...state,
{
name: 'new',
id: nextToDo+=1
}
]
default:
return state;
}
}
export default reducer
store.js
import reducer from './rootReducer'
import {applyMiddleware,createStore} from 'redux'
import logger from 'redux-logger'
import {persistStore, persistReducer} from 'redux-persist'
import storage from 'redux-persist/lib/storage'
const persistConfig ={
key: 'root',
storage
}
export const persistedReducer = persistReducer(persistConfig, reducer)
export const store = createStore(persistedReducer, applyMiddleware(logger));
export const persistor = persistStore(store);
export default {store, persistor}
Index.js:
import React, {Component} from 'react'
import {createStore} from 'redux'
import reducer from './rootReducer'
import 'bootstrap/dist/css/bootstrap.min.css'
import {Button} from 'react-bootstrap'
import {store} from './store'
import {connect} from 'react-redux'
class Index extends Component {
render(){
const {tasks} = this.props
const add=()=>
{
store.dispatch(
{
type: 'add',
}
)
}
store.subscribe(()=>console.log('your store is now', store.getState()))
return (
<div>
{tasks.map(task=><div>{task.name}</div>)}
<Button onClick={()=>add()}></Button>
</div>
)
}
}
const mapStateToProps=(state)=>
{
return{
tasks: state
}
}
export default connect(mapStateToProps)(Index)
Try to declare state this way:
state = {
tasks: [{}]
}
Then, on Index.js, you can pass it to props by doing this:
const mapStateToProps=(state)=>
{
return {
tasks: state.tasks
}
}
And use it inside the Component with props.tasks
Related
So I am making a to-do list in React in order to learn redux and I've been wondering how can I use something like the map function to map over the state, so that I can show the data stored within in different divs?
here's my initialState:
const tasks=
[
]
And then there's my reducer:
const taskReducer = (state=tasks,action) =>
{
switch(action.type){
case 'addTask':
return[
...state,
{
id: nextToDo+=1,
text: action.text,
completed: false
}
]
default:
return state;
}
}
What I want to do is something among the lines of:
{tasks.map(task=>
<div>
<h1>{task.text}</h1>
<div>)}
But it doesn't really work, what are some ways I can accomplish this?
You also use redux connect() method.
You can export default connect(mapStatetoProps)
for moreInfo:https://react-redux.js.org/api/connect
I found the solution people, here's the deal, I had to use mapStateToProps. I also had to change from functional to class component. So basically I first split my app in seperate files, one for the reducers and one for the store, here are all my files:
App.js:
import React from 'react';
import './App.css';
import tasksApp from './tasksApp';
import {Provider} from 'react-redux'
import store from './store'
function App() {
return (
<div>
<Provider store={store}>
<tasksApp></tasksApp>
</Provider>
</div>
);
}
export default App;
TasksApp.js:
import React, {Component} from 'react'
import 'bootstrap/dist/css/bootstrap.min.css'
import {Button} from 'react-bootstrap'
import {connect} from 'react-redux'
import $ from 'jquery'
import store from './store'
class tasksApp extends Component {
render(){
console.log(this.props)
const {tasks} = this.props
let nextToDo = 0;
const addTask=()=>
{
store.dispatch({
type: 'addTask',
text: $('.form-control').val()
})
}
return (
<div>
<h1>Enter Task</h1>
<input className='form-control' placeholder='Enter task'></input>
<Button variant='success' onClick={()=>addTask()}>Add Task</Button>
{tasks.map(task=>
<div key={task.id}>
<h1>{task.text}</h1>
<h2>{task.id}</h2>
<h3>{task.completed}</h3>
</div>)}
</div>
)
}
}
const mapStateToProps=(state)=>
{
return{
tasks: state
}
}
export default connect(mapStateToProps)(tasksApp)
store.js:
import {createStore} from 'redux'
import taskReducer from './reducers'
const store = createStore(taskReducer)
store.subscribe(()=>
console.log('Your store is now', store.getState())
)
export default store
reducers.js:
import store from './store'
import $ from 'jquery'
let nextToDo = 0;
const tasks=
[
{
id: nextToDo+=1,
text: 'action.text',
completed: false
}
]
const taskReducer = (state=tasks,action) =>
{
switch(action.type){
case 'addTask':
return[
...state,
{
id: nextToDo+=1,
text: $('.form-control').val(),
completed: false
}
]
default:
return state;
}
}
export default taskReducer;
Im using Redux with React Native to manage state. I believe that I've successfully set up the store and Provider. I can use store.getState() and store.dispatch(action()) from any component successfully, however, the react-redux connect function is not allowing me to access the store from child components. Can you find anything wrong with my code below?
Login.js - This child component I'm testing won't access redux store with react-redux connect.
import React, {Component} from 'react';
import actions from '../../redux/actions';
import {connect} from 'react-redux';
const mapStateToProps = state => {
// To test if this function fires, which it is not
console.log('login state mapping through redux');
return {
state: state,
};
};
const dispatchToProps = dispatch => {
return {
userRecieved: (user) => dispatch(actions.userRecieved(user)),
};
};
export class Login extends Component {
constructor(){
super();
this.state = {
credentials: {
email: '',
password: '',
},
};
}
componentDidMount(){
// This will show whether redux is connected
console.log(this.props.state);
this.props.userRecieved('TEST USER');
}
render() {
return ( <Text>{this.props.state}</Text> );
}
}
export default connect(mapStateToProps, dispatchToProps)(Login);
App.js
import React, {Component} from 'react';
import YEET from './src/YEET.js';
import store from './src/redux/stores/index';
import {Provider} from 'react-redux';
export default class App extends Component {
render() {
return (
<Provider store={store}>
<YEET />
</Provider>
);
}
}
My Redux Files:
store.js
import { combineReducers, createStore} from 'redux';
import accountReducer from '../reducers/accountReducer';
import postReducer from '../reducers/postReducer';
const initialState = {};
const reducers = combineReducers({
account: accountReducer,
post: postReducer,
});
const store = createStore(reducers, initialState);
export default store;
actions.js
import constants from '../constants';
var userRecieved = user => ({
type: constants.USER_RECIEVED,
data: user,
});
export default {
userRecieved,
};
accountReducer.js
import constants from '../constants';
var initialState = {
user: {
photos: [],
},
};
export default (state = initialState, action ) => {
let newState = Object.assign({}, state);
switch (action.type) {
case constants.USER_RECIEVED:
const user = {
id: action.data.uid,
// photos: action.data,
};
console.log(action);
newState.user = user;
return newState;
default:
return state;
}
};
From what I see, the only reason could be that you're importing the unconnected component.
When you import the Login component, make sure that you import the default export instead of the named export.
So, wherever you import the Login component, do it like this:
import Login from 'your-login-component-location/Login'
instead of
import { Login } from 'your-login-component-location/Login'
The second one is a named export, which will return the Login class directly.
The first one is the default export, which will return the connected component.
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.
On writing my first RN application, I have got the below error message on executing the code,
"undefined is not a function (evaluating '_ConfigureStore2.default.dispatch(CategoryAction.categoryView())')"
ConfigureStore.js:
import {createStore, applyMiddleware} from 'redux';
import reducers from '../reducers';
import thunk from 'redux-thunk';
var middlewares = applyMiddleware(thunk);
export default function configureStore(initialState) {
return createStore(reducers, initialState, middlewares);
}
CategoryContainer.js:
import React, { Component } from 'react';
import stores from '../stores/ConfigureStore';
import * as CategoryAction from '../actions/CategoryAction';
stores.dispatch(CategoryAction.categoryView());
class CategoryContainer extends Component {
}
CategoryAction.js:
import * as actionTypes from './ActionTypes';
import AppConstants from '../constants/AppConstants';
export function categoryView() {
const categories = ['CATEGORY1', 'CATEGORY2'];
return {
type: "CATEGORY_VIEW",
categories: categories
};
}
CategoryReducer.js:
const initialState = {
categories:[],
}
export default function categoryReducer (state = initialState, action) {
switch (action.type) {
case CATEGORY_VIEW:
return Object.assign({}, state, {
categories: action.categories
});
}
}
Even i tried the below approach in CategoryContainer.js, but still got the same error,
import { categoryView } from '../actions/CategoryAction';
stores.dispatch(categoryView());
Kindly assist to solve the issue.
Give this a shot:
CategoryAction.js
export default class CategoryAction {
categoryView = () => {
const categories = ['CATEGORY1', 'CATEGORY2'];
return {
type: "CATEGORY_VIEW",
categories: categories
};
}
}
CategoryContainer.js
import React, { Component } from 'react';
import stores from '../stores/ConfigureStore';
import CategoryAction from '../actions/CategoryAction';
stores.dispatch(CategoryAction.categoryView());
class CategoryContainer extends Component {
}
On changing the ConfigureStore.js as below solves the issue.
import {createStore, applyMiddleware} from 'redux';
import reducers from '../reducers';
import thunk from 'redux-thunk';
/*var middlewares = applyMiddleware(thunk);
export default function configureStore(initialState) { //default is undefined
return createStore(reducers, initialState, middlewares);
}*/
const createStoreWithMiddleware = applyMiddleware(thunk)(createStore);
const store = createStoreWithMiddleware(reducers);
export default store;
My previous React-Redux implementation was working, but after I tried to implement the combineReducer function with seperated files, an error is thrown that I don´t really understand. Hope some of you can help me!
ERROR: Uncaught TypeError: this.props.todos.map is not a function
My Reference for that Code was the Async Example of the Redux-Doc´s. But I stated with another example and the change from each examples are not documented in the doc´s.
The first code I will show, is that I had (working):
MyStore
import { createStore, applyMiddleware } from 'redux'
import thunkMiddleware from 'redux-thunk'
import createLogger from 'redux-logger'
import addItem from '../reducers/addItem'
export default function configureStore(preloadedState) {
const store = createStore(
addItem,
preloadedState,
applyMiddleware(thunkMiddleware, createLogger())
)
if (module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextRootReducer = require('../reducers').default
store.replaceReducer(nextRootReducer)
})
}
return store
}
My Reducer
export default (state = ['Test'], action) => {
switch (action.type){
case 'ADD_ITEM':
//return action.item
return [
...state,
{
id: action.id,
text: action.item
}
]
default:
return state
}
}
Actions
export default function addItem(item){
console.log("addTOdo")
return {
type: 'ADD_ITEM',
id: nextTodoId++,
item
}
}
And the subComponent where the input is finally rendered
import React, { Component, PropTypes } from 'react'
import { connect } from 'react-redux'
export default class TodoList extends Component {
render() {
const posts = this.props
const isEmpty = posts.length === 0
return (
<div>
<h3>Meine Aufgaben</h3>
<ul>
{isEmpty
? <h3>Sie haben noch keinen Todo´s angelegt</h3>
: <h3>Ihre Ergebnisse</h3>
}
{this.props.todos.map((todo, i) => <li key={i}>{todo.text} </li>)}
</ul>
</div>
)
}
}
const mapStateToProp = state => ({todos: state})
export default connect (mapStateToProp)(TodoList)
What I have change:
First, I created another Reducers File, called Index where I imported the addItem Reducer and exported the rootReducer:
import {combineReducers} from 'redux'
import addItem from './addItem'
import getItem from './getItem'
const rootReducer = combineReducers({
addItem,
getItem
})
export default rootReducer
After that, I changed the Store to import the rootReducer and put it´s reference in the Store (just the changes to configureStore):
import rootReducer from '../reducers/index'
const store = createStore(
rootReducer,
preloadedState,
applyMiddleware(thunkMiddleware, createLogger())
)
I don´t know if that Information is also required, but here is my Container Component:
import React, { Component, PropTypes } from 'react'
import AddTodo from '../components/AddTodo'
import TodoList from '../components/TodoList'
import { connect } from 'react-redux'
import addItem from '../actions/addItem'
import getItems from '../actions/getItems'
class App extends Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
this.state = {text: ''}
}
handleClick(e){
console.log(e);
const {dispatch} = this.props
dispatch(addItem(e));
}
componentDidMount(){
console.log("COMPONENT MOUNT");
const {dispatch} = this.props
// dispatch(getItems())
}
componentWillReceiveProps(nextProps) {
console.log("GETTT IT");
console.log(nextProps)
}
render() {
return (
<div>
< h1 > Hallo </h1>
<AddTodo handleAddItem={this.handleClick}/>
<TodoList/>
</div>
)
}
}
App.propTypes = {
dispatch: PropTypes.func.isRequired
}
function mapStateToProps(state){
return {
AddTodo
}
}
export default connect (mapStateToProps)(App)
I hope this issue is not to basic and someone can help me. Thanks in advance!
If you inspect your redux state you will see that the following code sets up 2 more keys in the state (addItem and getItem):
const rootReducer = combineReducers({
addItem,
getItem
})
So, now to connect todos you need to one of the 2 new keys. If todos is not defined on those, then you need to add the reducer of todos to the combineReducers call.
So this needs to map to a valid location in state:
const mapStateToProp = state => ({todos: state})