首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >获取错误state.stories不可迭代

获取错误state.stories不可迭代
EN

Stack Overflow用户
提问于 2021-04-21 15:45:10
回答 1查看 309关注 0票数 0

我正在创建一个react/redux应用程序,用户可以登录,并能够通过提交表单添加故事。故事以对象的形式提交,我想将该故事添加到数组的对象中。但是,即使我在reducer中编写stories: [...state.stories, action.payload],我也总是收到错误,提示为state.stories is not iterable。action.payload返回一个对象,我想将该对象放入一个数组中。如何将action.payload对象插入到数组中?我希望故事是一个对象数组。我对任何糟糕的格式表示歉意。

App.js

代码语言:javascript
复制
import './App.scss';
import Login from './components/Login';
import { Router, Switch, Route, NavLink } from 'react-router-dom';
import PrivateRoute from './utils/PrivateRoute';
import CreateStory from './components/CreateStory';
import history from './utils/history';

function App() {


  return (
    <div className="App">

      <Router history={history}>
        <Switch>
          <Route exact path="/" component={Login} />
          <PrivateRoute path="/user" component={CreateStory}/>

        </Switch>
      </Router>
    </div>
  );
}

export default App;

PrivateRoute.js

代码语言:javascript
复制
import { useSelector } from 'react-redux'
 
// handle the private routes
function PrivateRoute({ component: Component, ...rest }) {

  const getToken = useSelector((state)=> state.loginReducer.token)
  console.log(getToken)
  return (
    <Route
      {...rest}
      render={(props) => getToken ? <Component {...props} /> : <Redirect to={{ pathname: '/', state: { from: props.location } }} />}
    />
  )
}
 
export default PrivateRoute;

CreateStory.js

代码语言:javascript
复制
    import React, { useState } from 'react'
import { createStory } from '../redux/actions'
import { useDispatch, useSelector } from "react-redux";
import history from '../utils/history';
import { withRouter } from 'react-router-dom';

const CreateStory = () => {

    const [summary, setSummary] = useState("");
    const [description, setDescription] = useState("");
    const [type, setType] = useState("");
    const [complexity, setcomplexity] = useState("");
    const [time, setTime] = useState("");
    const [cost, setCost] = useState(0);

    const usedispatch = useDispatch();
    const userCreateStory = (summary, description, type, complexity) => usedispatch(createStory({
                                                                                    'summary': summary,
                                                                                    'description': description,
                                                                                    'type': type,
                                                                                    'complexity': complexity,
                                                                                    'time': time,
                                                                                    'cost': cost 
                                                                                }));

    const handleSummaryChange = e => {
        setSummary(e.target.value)
    }  
    
    const handleDescriptionChange = e => {
        setDescription(e.target.value)
    }

    const handleTypeChange = e => {
        setType(e.target.value)
    }

    const handleComplexityChange = e => {
        setcomplexity(e.target.value)
    }

    const handleTimeChange = e => {
        setTime(e.target.value)
    }

    const handleCostChange = e => {
        setCost(e.target.value)
    }

   // const currStory = useSelector((state)=> state.storyReducer.story)
    const handleSubmit = e => {
        e.preventDefault();
        userCreateStory(summary,description,type,complexity,time,cost)
        setTimeout(()=> history.push("/userStories"), 1000 );
      //setTimeout(()=> console.log(currStory) ,1000)
    }

    
    
    return (
        <div>
            <form className='create-story-form'>
                <label for="summary">Summary:</label>
                <input name="summary" type='text' onChange={handleSummaryChange}/>
                <label for="desc">Description:</label>
                <textarea name="desc" type='text' onChange={handleDescriptionChange}/>
                <label for="type">Type:</label>
                <select name="type" onChange={handleTypeChange}>
                    <option value="enhancement" defaultValue>Enchancement</option>
                    <option value="bugfix">Bugfix</option>
                    <option value="development">Development</option>
                    <option value="qa">QA</option>
                </select>
                <label for="complexity">Complexity:</label>
                <select name="complexity" onChange={handleComplexityChange}>
                    <option value="low" defaultValue>Low</option>
                    <option value="mid">Mid</option>
                    <option value="high">High</option>
                </select>
                <label for="time">Estimated time for completion:</label>
                <input name="time" type='text' onChange={handleTimeChange}/>
                <label for="cost">Cost:</label>
                <input name="cost" type='number' onChange={handleCostChange}/>
                <button onClick={handleSubmit}>Submit</button>
            </form>
        </div>
    )
}

export default withRouter(CreateStory);

Login.js

代码语言:javascript
复制
import React, { useState } from "react";
import { useDispatch } from "react-redux";
import { login, roleChange } from '../redux/actions' //OUR ACTIONS
import { useSelector } from 'react-redux'
import history from '../utils/history';
import { withRouter } from 'react-router-dom';

const Login = () => {

    const [email, setEmail] = useState("");
    const [password, setPassword] = useState("");

    const usedispatch = useDispatch();
    const userLogin = (email, password) => usedispatch(login({'email': email, 'password': password }));
    const switchToAdmin = () => usedispatch(roleChange('admin'));
    const switchToUser = () => usedispatch(roleChange('user'));
    const currentRole = useSelector((state)=> state.loginReducer.role)

    const handleRoleChange = e => {
        e.preventDefault();
        if(currentRole === 'user')
            switchToAdmin();
        else if(currentRole === 'admin' )
            switchToUser()
    }
    
    const handleEmailChange = e => {
        setEmail(e.target.value)
    }

    const handlePasswordChange = e => {
        setPassword(e.target.value)
    }

    const handleSubmit = e => {
        e.preventDefault();
        userLogin(email, password)
        setTimeout(()=> history.push("/user"), 1000 );
    }

    const disabled = () => {
        return email === "" || password === ""
    }

   

    return (
        <div>
            <form className='login-form'>
                <input type='email' name='email' placeholder='Email' onChange={handleEmailChange}/>
                <input type='password' name='password' placeholder='Password' onChange={handlePasswordChange}/>
                <button type='submit' disabled={disabled()} onClick={handleSubmit}>Login</button>
            </form>
            <button onClick={handleRoleChange}>Switch to {currentRole === 'user' ? 'admin' : 'user'}</button>
        </div>
    )
}

export default withRouter(Login);

actionTypes.js

代码语言:javascript
复制
export const SET_LOGIN_STATE = "SET_LOGIN_STATE"
export const SET_ROLE_STATE = "SET_ROLE_STATE"
export const CREATE_STORY = "CREATE_STORY"

initialState.js:

代码语言:javascript
复制
import { getToken } from '../utils/Common'

export const initialState = {
    isLoggedIn: false,
    userId: '',
    role: 'user',
    token: getToken,
    data: '',
  };

reducers.js

代码语言:javascript
复制
import { initialState } from './initialState';
import * as t from './actionTypes';

export const loginReducer = (state = initialState, action) => {
  switch (action.type) {
    case t.SET_ROLE_STATE:
      return {
        ...state,
        role: action.payload,
      };
    case t.SET_LOGIN_STATE:
      return {
        ...state,
        ...action.payload, // this is what we expect to get back from API call and login page input
        isLoggedIn: true, // we set this as true on login
      };
    default:
      return state;
  } 
};

export const storyReducer = (state = initialState, action) => {
  switch (action.type) {
    case t.CREATE_STORY:
      return {
        ...state,
        story: [...state.stories, action.payload],   //the error pops up at this line
      };
    default:
      return state;
  } 
}

actions.js:

代码语言:javascript
复制
import * as t from './actionTypes';
import { setUserSession } from '../utils/Common';

// this is what our action should look like which dispatches the "payload" to reducer
const setLoginState = (loginData) => {
  return {
    type: t.SET_LOGIN_STATE,
    payload: loginData, //{ ...json, userId: email }
  };
};

const setStoryState = (storyData) => {
    return {
      type: t.CREATE_STORY,
      payload: storyData,
    };
  };

export const login = (loginInput) => { //our login action
    const { email, password } = loginInput;
    return (dispatch) => {  // don't forget to use dispatch here!
      return fetch('http://localhost:3000/api/v1/signin', {
        method: 'POST',
        headers: {  
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(loginInput),
      })
        .then((response) => response.json()) //json will be the response body
        .then((json) => {
        // if (json.msg === 'success') { // response success checking logic could differ
           // console.log(json)
            dispatch(setLoginState({ ...json, userId: email })); // our action is called here with object as parameter, this is our payload
            //we appended json object to our state
            //   } else {
        //     alert('Login Failed', 'Email or Password is incorrect');
        //  }
            setUserSession(json.token, json.lastName)
        })
        .catch((err) => {
          alert('Login Failed', 'Some error occured, please retry');
          console.log(err);
        });
    };
};

export const roleChange = role => {
    return {
        type: t.SET_ROLE_STATE,
        payload: role
      };
}



  export const createStory = storyInput => {
    const { summary, description, type, complexity, time, cost } = storyInput;
    return (dispatch) => {  // don't forget to use dispatch here!
      return fetch('http://localhost:3000/api/v1/stories', {
        method: 'POST',
        headers: {  
          Accept: 'application/json',
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${token}`,
        },
        body: JSON.stringify(storyInput),
      })
        .then((response) => response.json()) //json will be the response body
        .then((json) => {
        // if (json.msg === 'success') { // response success checking logic could differ
            console.log(json)
            dispatch(setStoryState({  // our action is called here with object as parameter, this is our payload
                summary: summary,
                description: description,
                type: type,
                complexity: complexity,
                time: time,
                cost: cost
            })); // our action is called here
        //   } else {
        //     alert('Login Failed', 'Email or Password is incorrect');
        //  }
        })
        .catch((err) => {
          alert('Some error occured, please retry');
          console.log(err);
        });
    };
}

Common.js

代码语言:javascript
复制
// return the user data from the session storage
export const getUser = () => {
    const userStr = sessionStorage.getItem('user');
    if (userStr) return JSON.parse(userStr);
    else return null;
}
   
// return the token from the session storage
export const getToken = () => {
    return sessionStorage.getItem('token') || null;
}
   
// remove the token and user from the session storage
export const removeUserSession = () => {
    sessionStorage.removeItem('token');
    sessionStorage.removeItem('user');
}
   
// set the token and user from the session storage
export const setUserSession = (token, user) => {
    sessionStorage.setItem('token', token);
    sessionStorage.setItem('user', JSON.stringify(user));
}

更新:我已将storyReducer更改为:

代码语言:javascript
复制
export const storyReducer = (state = {stories: []}, action) => {
  switch (action.type) {
    case t.CREATE_STORY:
      return {
        ...state,
        stories: [...state, action.payload],
      };
    // case t.ADD_STORY:
    //   return {
    //     ...state,
    //     stories: [...state.stories, action.payload], //stories is an object
    //   };
    default:
      return state;
  } 
}

现在,当添加第一个故事时,我没有得到错误。但是当我向数组中添加超过1个存储时,我得到了错误。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-04-21 16:23:00

您为您的loginReducerstoryReducer使用了相同的initialState -后者实际上无法使用它,因为它没有stories属性,因此您试图传播undefined

使用两种不同的初始状态。它们也不一定要有那个名字,你可以随心所欲地命名它们;)

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67191408

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档