I have been trying to create a React app using redux and redux-saga, but I haven't been able to make it work, and I alway get the value of undefined as the result.
This is my component catalogs/index.js:
import React, {Component} from 'react';
import {connect} from 'react-redux';
import CatalogHeader from './CatalogHeader';
import CircularProgress from '#material-ui/core/CircularProgress';
import {getCatalogs} from 'actions/Catalogs';
import IntlMessages from 'util/IntlMessages';
import CustomScrollbars from 'util/CustomScrollbars';
class Catalogs extends Component {
constructor() {
super();
this.state = {
anchorEl: null
}
}
updateSearch = (evt) => {
this.props.updateSearch(evt.target.value);
this.onSearchTodo(evt.target.value)
};
render() {
const {catalogsList} = this.props;
return (
<div className="app-wrapper">
<div className="animated slideInUpTiny animation-duration-3">
<div className="app-module">
<div className="module-box">
<div className="module-box-header">
<CatalogHeader catalogsList={catalogsList}
placeholder="Buscar" user={this.props.user}
onChange={this.updateSearch.bind(this)}
value={this.props.searchTodo}/>
</div>
</div>
</div>
</div>
</div>
)
}
}
const mapStateToProps = ({catalogs, settings}) => {
const {width} = settings;
const {catalogsList} = catalogs;
return {
width,
catalogsList,
}
};
export default connect(mapStateToProps, {
getCatalogs,
})(Catalogs);
This is the other component catalogs/CatalogHeader.js
import React from 'react';
import IconButton from '#material-ui/core/IconButton';
import Button from '#material-ui/core/Button';
import {Dropdown, DropdownMenu, DropdownToggle, Popover} from 'reactstrap';
import SearchBox from 'components/SearchBox';
import Input from '#material-ui/core/Input';
import InputLabel from '#material-ui/core/InputLabel';
import MenuItem from '#material-ui/core/MenuItem';
import FormControl from '#material-ui/core/FormControl';
import FormHelperText from '#material-ui/core/FormHelperText';
import Select from '#material-ui/core/Select';
class CatalogHeader extends React.Component {
handleChange = name => event => {
this.setState({[name]: event.target.value});
};
onSearchBoxSelect = () => {
this.setState({
searchBox: !this.state.searchBox
})
};
constructor() {
super();
this.state = {
anchorEl: undefined,
searchBox: false,
popoverOpen: false
};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState({
popoverOpen: !this.state.popoverOpen
});
}
printCatalogs(catalogs) {
console.log(catalogs);
}
render() {
const {catalogs} = this.props;
return (
<div className="module-box-header-inner catalogs-header">
<div className="col-5 catalogs-header">
<FormControl className="w-100 mb-2">
{this.printCatalogs(this.props.catalogs)}
<InputLabel htmlFor="age-simple">Seleccione Catálogo</InputLabel>
<Select
value={this.state.age}
onChange={this.handleChange('age')}
input={<Input id="ageSimple1"/>}>
{catalogs.map(catalog =>
<MenuItem key={catalog}>
{catalog}
</MenuItem>,
)}
<MenuItem value="">
<em>None</em>
</MenuItem>
<MenuItem value={10}>Ten</MenuItem>
<MenuItem value={20}>Twenty</MenuItem>
<MenuItem value={30}>Thirty</MenuItem>
</Select>
</FormControl>
</div>
<div className="module-box-header-right col-7 catalogs-header">
<div className="search-bar right-side-icon bg-transparent d-none d-sm-block col-6">
<div className="form-group">
<input className="form-control border-0" type="search" placeholder="Buscar"/>
<button className="search-icon"><i className="zmdi zmdi-search zmdi-hc-lg"/></button>
</div>
</div>
<div className="col-6">
<div className="catalogs-header catalogs-header-buttons">
<Button variant="contained" className="jr-btn bg-white">
<i className="zmdi zmdi-filter-list zmdi-hc-fw"/>
<span>Filtrar</span>
</Button>
<Button variant="contained" className="jr-btn bg-white">
<i className="zmdi zmdi-sort zmdi-hc-fw"/>
<span>Ordenar</span>
</Button>
</div>
</div>
</div>
</div>
)
}
}
export default CatalogHeader;
This is my actions file:
import {
GET_CATALOGS,
GET_CATALOGS_SUCCESS,
SHOW_MESSAGE
} from 'constants/ActionTypes';
export const getCatalogs = (group) => {
return {
type: GET_CATALOGS,
payload: group
};
};
export const getCatalogsSuccess = (catalogs) => {
return {
type: GET_CATALOGS_SUCCESS,
payload: catalogs
}
};
export const showCatalogsMessage = (message) => {
return {
type: SHOW_MESSAGE,
payload: message
};
};
This is my reducers file:
import {
GET_CATALOGS,
SHOW_MESSAGE,
HIDE_MESSAGE,
GET_CATALOGS_SUCCESS
} from 'constants/ActionTypes';
const INIT_STATE = {
loader: false,
alertMessage: '',
showMessage: false,
catalogsList: null
};
export default (state=INIT_STATE, action) => {
switch (action.type) {
case GET_CATALOGS_SUCCESS: {
return {
...state,
loader: false,
catalogsList: action.payload
}
}
case SHOW_MESSAGE: {
return {
...state,
alertMessage: action.payload,
showMessage: true,
loader: false
}
}
case HIDE_MESSAGE: {
return {
...state,
alertMessage: '',
showMessage: false,
loader: false
}
}
default:
return state;
}
}
This is my sagas file:
import {all, call, fork, put, takeEvery} from "redux-saga/effects";
import {catalogs} from 'backend/Catalogs';
import {GET_CATALOGS} from "constants/ActionTypes";
import {getCatalogs, getCatalogsSuccess, showCatalogsMessage} from 'actions/Catalogs';
const getCatalogsRequest = async (group) => {
await catalogs.getCatalogs(group)
.then(catalogsList => catalogsList)
.catch(error => error);
}
function* getCatalogsListFromRequest({payload}) {
const {group} = payload;
try {
const catalogsList = yield call(getCatalogsRequest, group);
if (catalogsList.message) {
yield put(showCatalogsMessage(catalogsList.Message));
} else {
yield put(getCatalogsSuccess(catalogsList.catalogsList))
}
} catch (error) {
yield put(showCatalogsMessage(error));
}
}
export function* getCatalogsList() {
yield takeEvery(GET_CATALOGS, getCatalogsListFromRequest);
}
export default function* rootSaga() {
yield all([
fork(getCatalogsList)
]);
}
And this is the file that performs the ajax request through Axios (I know for sure that this code is never reached, and a request to the backend server is never performed):
import axios from 'axios';
import {backendServer} from './constants';
const getCatalogsEndpoint = backendServer + 'api/util/catalogs/';
const getCatalogs = (group) => {
console.log('here inside getCatalogs in backend/Catalogs');
return axios.get(getCatalogsEndpoint+(group=null)?null:'?group='+group)
.then(Response => {
return {catalogsList: Response.data}
})
.catch(Error => Error);
};
export const catalogs = {
getCatalogs: getCatalogs
}
The problem arises in CatalogsHeader.js, because this.props.catalogsList always has the value of undefined:
I see problem in CatalogHeader. I think, you are referring a wrong pop.
In index.js you are passing the prop as catalogsList={catalogsList} to CatalogHeader.
But in CatalogHeader you are accessing prop as const {catalogs} = this.props; i.e. catalogs which is undefined. Please try changing this into catalogsList.
Related
I have a login page (login.js), when the form is submited data gets sent using redux , dispatch but when i console.log user selector it shows undefined and i don't really know why. The ajax call work and resp.data.json show the data in saga.js.
Init state is defined so even before the ajax call i should be able to access to isLoading and errorMessage which i cannot even have access.
login.js
import React, { Component, useState, useEffect } from 'react';
import { Link } from 'react-router-dom';
import { useParallax } from "react-scroll-parallax";
import { useTranslation } from "react-i18next";
import axios from "axios";
import "./Login.css";
import logoWhite from './img/logo-white.png';
import svgLogo from './img/log.png';
import { Helmet } from 'react-helmet';
import { useDispatch, useSelector } from 'react-redux';
import userActions from './redux/auth/actions';
const {
postToApi,
} = userActions;
function Login() {
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const user = useSelector(
state => state.user
);
const dispatch = useDispatch();
const handleLogin = e => {
e.preventDefault();
var data = {};
data.email = email;
data.password = password;
dispatch(postToApi(data));
console.log(user); // Undefined
};
const { t, i18n } = useTranslation();
return (
<div className="login-page">
<Helmet>
<title>Espace Client - Développement de site internet à Antibes, Cannes, Nice</title>
</Helmet>
<div className="container">
<div className="forms-container">
<div className="signin-signup">
<form onSubmit={handleLogin} autoComplete="off" className="sign-in-form">
<h2 className="title">Identifiez-vous</h2>
<div className="input-field">
<i className="fas fa-user"></i>
<input type="text" value={email} onInput={e => setEmail(e.target.value)} placeholder="Adresse e-mail" autoComplete="off" />
</div>
<div className="input-field">
<i className="fas fa-lock"></i>
<input type="password" value={password} onInput={e => setPassword(e.target.value)} placeholder="Mot de passe" autoComplete="new-password" />
</div>
<button type="submit" className="btn solid">CONTINUER</button>
</form>
</div>
</div>
<div className="panels-container">
<div className="panel left-panel">
<div className="content">
<Link to="/"><img src={logoWhite} className="logo" alt="Création de sites Web vitrine" /></Link>
<h3 className="text-white">ESPACE CLIENT</h3>
<p>
Vous pourrez suivre vos factures, modifier vos informations personelles, trouver de l'aide...
</p>
</div>
<img src={svgLogo} className="image" alt="Création de sites Web vitrine" />
</div>
</div>
</div>
</div>
)
}
export default Login;
actions.js
const actions = {
POST_TO_API: 'POST_TO_API',
POST_TO_API_SUCCESS: 'POST_TO_API_SUCCESS',
POST_TO_API_ERROR: 'POST_TO_API_ERROR',
postToApi: data => {
return {
type: actions.POST_TO_API,
payload: { data },
};
},
postToApiSuccess: data => ({
type: actions.POST_TO_API_SUCCESS,
payload: { data },
}),
postToApiError: error => ({
type: actions.POST_TO_API_ERROR,
payload: { error },
}),
};
export default actions;
reducer.js
import actions from './actions';
const initState = {
isLoading: false,
errorMessage: false,
data: []
};
export default function reducer(
state = initState,
{ type, payload }
) {
switch (type) {
case actions.POST_TO_API:
return {
...state,
isLoading: true,
errorMessage: false
};
case actions.POST_TO_API_SUCCESS:
return {
...state,
isLoading: false,
data: payload.data,
errorMessage: false
};
case actions.POST_TO_API_ERROR:
return {
...state,
isLoading: false,
errorMessage: 'There is a problem'
};
default:
return state;
}
}
saga.js
import { all, takeEvery, put, call } from 'redux-saga/effects';
import actions from './actions';
import axios from "axios";
function run(data){
var actionUrl = '/pages/login';
return axios ({
method: 'POST',
url: 'http://xzy/api' + actionUrl,
data: {
data
},
headers: { 'Content-Type': 'application/json;charset=UTF-8', "Access-Control-Allow-Origin": "*", "Accept": "application/json" }
});
}
function* postToApi(payload) {
try {
const resp = yield call(run, payload.payload.data)
console.log(resp.data.json); // Data shown in the log
yield put(actions.postToApiSuccess(resp.data.json));
} catch (error) {
yield put(actions.postToApiError(error));
}
}
export default function* rootSaga() {
yield all([
takeEvery(actions.POST_TO_API, postToApi),
]);
}
App.js
import React from 'react';
import { Provider } from 'react-redux';
import { store } from './redux/store';
import Routes from './router';
import AppProvider from './AppProvider';
const App = () => (
<Provider store={store}>
<AppProvider>
<>
<Routes />
</>
</AppProvider>
</Provider>
);
export default App;
AppProvider.js
import React from 'react';
import { ConfigProvider } from 'antd';
export default function AppProvider({ children }) {
return (
<ConfigProvider locale="fr">
{children}
</ConfigProvider>
);
}
./redux/store.js
import { createStore, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import createSagaMiddleware from 'redux-saga';
import rootReducer from './root-reducer';
import rootSaga from './root-saga';
const sagaMiddleware = createSagaMiddleware();
const middlewares = [thunk, sagaMiddleware];
const bindMiddleware = middleware => {
if (process.env.NODE_ENV !== 'production') {
const { composeWithDevTools } = require('redux-devtools-extension');
return composeWithDevTools(applyMiddleware(...middleware));
}
return applyMiddleware(...middleware);
};
const store = createStore(rootReducer, bindMiddleware(middlewares));
sagaMiddleware.run(rootSaga);
export { store };
root-saga.js
import { all } from 'redux-saga/effects';
import authSagas from './auth/saga';
export default function* rootSaga(getState) {
yield all([
authSagas()
]);
}
root-reducer.js
import { combineReducers } from 'redux';
import App from './app/reducer';
import Auth from './auth/reducer';
export default combineReducers({
Auth,
App
});
Note: please pull the repo to get access to the full App, there are too many files to simply share the code here. The repo: https://github.com/liviu-ganea/movie-database.git
So I've asked a question previously about my first React + Redux app and I've improved it. I got 2 problems now. The delete function for each movie (i.e. each entry in the redux state) is working just fine. So I now have problems with Login. Every time I press the button, nothing happens, and I suspect it's because the Login page component receives no props from the redux state.
My reasoning:
handleCLick = () => {
let userName = this.props.user.nickname;
let pw = this.props.user.password;
const nicknameBox = document.getElementById('nickname').textContent;
const passwordBox = document.getElementById('password').textContent.trim;
/*if (nicknameBox === userName && passwordBox === pw) {*/
this.props.loginUser((this.props.user.isLoggedIn = true));
this.props.history.push('/');
//}
};
When commented as it is now, it should go to the home page whenever I click the login button and if the password matches doesn't matter. Only there is no reaction to me pressing the button. Is the problem as I suspect?
And another problem: see in Movie.js I've tried to get the path (it's set in state: movies: cover) to the movie poster (located in ./src) so that when (or if) I make use of an API the path should set itself dynamically (i.e. I won't have to go into every component and add the path manually). Same thing on the Home page (./src/Components/Routes/Home.js)...no posters for any movie.
After Comments:
Component:
import React, { Component } from 'react';
import './routes.css';
import { loginAction } from '../../actions/userActions';
import { connect } from 'react-redux';
class Login extends Component {
handleCLick = () => {
let userName = this.props.user.nickname;
let pw = this.props.user.password;
const nicknameBox = document.getElementById('nickname').textContent;
const passwordBox = document.getElementById('password').textContent.trim;
/*if (nicknameBox === userName && passwordBox === pw) {*/
this.props.loginUser((this.props.user.isLoggedIn = true));
this.props.history.push('/');
//}
};
render() {
console.log(this.props);
return (
<div className="container page-container login-page">
<h3 className="page-title">Log In User</h3>
<div className="login-box">
<div>
<label className="login-labels" htmlFor="nickname">
Login{' '}
</label>
<input type="text" className="nickname-box" name="nickname" id="nickname" />
</div>
<div>
<label className="login-labels" htmlFor="password">
Password{' '}
</label>
<input type="password" className="password-box" name="password" id="pasword" />
</div>
<button className="login-button" onClick={this.handleClick}>
Login
</button>
</div>
</div>
);
}
}
const mapStateToProps = (state) => {
return {
user: state.user,
};
};
const mapDispatchToProps = (dispatch) => {
return {
loginUser: (isLoggedIn) => {
dispatch(loginAction(isLoggedIn));
},
};
};
export default connect(mapDispatchToProps, mapStateToProps)(Login);
Reducer:
import { createStore } from 'redux';
const initialState = {
movies: [
{
id: '1',
title: 'John Wick Chapter 2',
year: '2017',
main_actor: 'Keanu Reeves',
summary: `The hitman that's been retired then back then retired strikes again, this time against the Mafia.`,
cover: '../john_wick_2.jpg',
},
{
id: '2',
title: 'Star Wars Revenge of the Sith',
year: '2005',
main_actor: 'Ewan McGregor',
summary: `Anakin betrays Space Jesus so General Kenobi is forced to Mustafar Anakin.`,
cover: '../sw_rots.png',
},
{
id: '3',
title: 'Star Wars The Clone Wars',
year: '2008 - 2020',
main_actor: 'Ewan McGregor, Hayden Christensen',
summary: `Yoda has finally overdosed on Ketamine, Mace Window hasn't been defenestrated yet and The Negotiator has proven himself incapable of falling to the Dark Side`,
cover: '../sw_tcw.jpg',
},
],
user: {
isLoggedIn: 'false',
nickname: 'obi_wan',
password: '12345',
},
};
const rootReducer = (state = initialState, action) => {
switch (action.type) {
case 'DELETE_MOVIE':
let newMovieList = state.movies.filter((movie) => {
return action.id !== movie.id;
});
return {
...state,
movies: newMovieList,
};
case 'LOG_IN':
let newUserState = action.isLoggedIn;
return {
...state,
user: { ...state.user, isLoggedIn: action.payload.isLoggedIn },
};
default:
return state;
}
return state;
};
export default rootReducer;
userActions:
export const loginAction = (isLoggedIn) => {
return {
type: 'LOG_IN',
isLoggedIn: 'true',
};
};
index.js:
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createStore } from 'redux';
import rootReducer from './reducers/rootReducer';
import App from './App';
const store = createStore(rootReducer, window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__());
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
);
console.log(this.props) for the Login component:
[![enter image description here][1]][1]
Movie.js:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { deleteMovieAction } from '../actions/movieActions';
import './Routes/routes.css';
class Movie extends Component {
handleClick = () => {
this.props.deleteMovie(this.props.movie.id);
this.props.history.push('/');
};
render() {
console.log(this.props);
const isUser = this.props.user.isLoggedIn ? (
<button className="delete-movie-button" onClick={this.handleClick}>
Delete
</button>
) : null;
const theMovie = this.props.movie ? (
<div className="movie-container">
<img src={this.props.movie.cover} alt={this.props.movie.title} className="movie-cover" />
<div className="movie-container-content">
<h2 className="movie-title">{this.props.movie.title}</h2>
<p className="movie-description">{this.props.movie.summary}</p>
<div className="movie-data">
<p className="movie-year">{this.props.movie.year}</p>
<p className="movie-actor">{this.props.movie.main_actor}</p>
</div>
</div>
{isUser}
</div>
) : (
<div className="center">Getting data about the movie. Please wait.</div>
);
return <div className="container page-container">{theMovie}</div>;
}
}
const mapStateToProps = (state, ownProps) => {
let id = ownProps.match.params.movie_id;
return {
user: state.user,
movie: state.movies.find((movie) => movie.id === id),
};
};
const mapDispatchToProps = (dispatch) => {
return {
deleteMovie: (id) => {
dispatch(deleteMovieAction(id));
},
};
};
export default connect(mapStateToProps, mapDispatchToProps)(Movie);
Movie.js - every movie component
import React, {Component} from 'react'
import { connect } from 'react-redux';
import { deleteMovieAction } from '../actions/movieActions'
import './Routes/routes.css'
class Movie extends Component {
handleClick = () => {
this.props.deleteMovie(this.props.movie.id);
this.props.history.push('/');
}
render () {
const coverino = ''
console.log(this.props);
const isUser = this.props.isLoggedIn ? ( <button className='delete-movie-button' onClick={this.handleClick}>Delete</button> ) : (null)
const theMovie = this.props.movie ? (
<div className='movie-container'>
<img src={this.props.movie.cover} alt={this.props.movie.title} className='movie-cover'/>
<div className='movie-container-content'>
<h2 className='movie-title'>{this.props.movie.title}</h2>
<p className='movie-description'>{this.props.movie.summary}</p>
<div className='movie-data'>
<p className='movie-year'>{this.props.movie.year}</p>
<p className='movie-actor'>{this.props.movie.main_actor}</p>
</div>
</div>
{isUser}
</div>
) : ( <div className="center">Getting data about the movie. Please wait.</div> );
return (
<div className='container page-container'>
{theMovie}
</div>
)
}
}
const mapStateToProps = (state, ownProps) => {
let id = ownProps.match.params.movie_id;
return {
isLoggedIn: state.user.isLoggedIn,
movie: state.movies.find(movie => movie.id === id),
}
}
const mapDispatchToProps = (dispatch) => {
return {
deleteMovie: (id) => { dispatch(deleteMovieAction(id))}
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Movie)
The page display the entire movie list
import React, {Component} from 'react'
import { Link } from 'react-router-dom'
import { connect } from 'react-redux'
import './routes.css'
class Home extends Component{
render() {
console.log(this.props);
const { movies } = this.props;
const movieList = movies.length ? ( movies.map(movie => {
return (
<Link to={'/' + movie.id} key={movie.id}>
<div className='movie-container'>
<img src={require(`${movie.cover}`)} alt={movie.title} className='movie-cover'/>
<div className='movie-container-content'>
<h2 className='movie-title'>{movie.title}</h2>
<p className='movie-description'>{movie.summary}</p>
<div className='movie-data'>
<p className='movie-year'>{movie.year}</p>
<p className='movie-actor'>{movie.main_actor}</p>
</div>
</div>
</div>
</Link>
)
}
, )) : (<div className='waiting-for-movies'>Loading. Please wait</div>)
return (
<div className='container page-container'>
{movieList}
</div>
)
}
}
const mapStateToProps = (state) => {
return {
movies: state.movies
}
}
export default connect(mapStateToProps)(Home)
neither the require method, nor the simple {this.props...} works
[1]: https://i.stack.imgur.com/51FTy.png
First Compoent
import React from "react";
import ReactDOM from "react-dom";
import PropTypes from 'prop-types'
import { withRouter } from "react-router-dom";
import { gateway as MoltinGateway } from "#moltin/sdk";
import {getList,updateList} from "./../Action/Action";
import { connect } from "react-redux";
import Icon from '#material-ui/core/Icon';
import Payment from "./../Payment/Payment";
import Tick from './done.png'
export class Item extends React.Component {
constructor(props) {
super(props);
this.state = {};
this.pickItem = this.pickItem.bind(this);
}
UpdateList ={}
pickItem(pickedItem, id) {
//console.log(pickedItem,id)
document.getElementById(id).classList.toggle("active")
this.UpdateList = pickedItem.map(function(data,i){
if(data.id == id && i<=5 && data.pickedItem!=='Yes'){
data.pickedItem = 'Yes'
return data
}else{
data.pickedItem = 'No'
return data
}
});
}
componentWillMount() {
this.props.getList();
}
updateList(){
//console.log(this.UpdateList)
this.props.updateList(this.UpdateList)
this.props.history.push({
pathname: '/Payment',
});
}
render() {
//const { pickedItem } = this.state;
const {list} = this.props
let filtereDate;
if(list!==undefined && list.length>0){
filtereDate = list.map(function(data,i){
if(i<=5){
return(
<div key={data.id} ref={data.id} id={data.id} onClick={this.pickItem.bind(this, list, data.id )} className='item-list'>
<span className="tickMark"><img src={Tick} /></span>
<div className="logoWarapper">
<img
src="https://rukminim1.flixcart.com/image/660/792/jmdrr0w0/shirt/q/q/r/xxl-tblwtshirtful-sh4-tripr-original-imaf9ajwb3mfbhmh.jpeg?q=50"
width="100"
height="100"
alt=""
/>
</div>
<div className="itemWarapper">
<h3>{data.name}</h3>
<p>
<span>₹</span>
<span>{data.id}</span>
</p>
</div>
</div>
)
}
}.bind(this));
}
return (
<div className="ItemPage">
<header>
<h1>Online shopping</h1>
<h2>Visit | Pick | Pay</h2>
</header>
{filtereDate}
<div className="btnWrp">
<button onClick={this.updateList.bind(this)} className="button">Make Payment</button>
</div>
</div>
);
}
}
Item.propTypes = {
list: PropTypes.object,
getList: PropTypes.func,
updateList:PropTypes.func
}
function mapStateToProps(state){
const Items= state
return {
list : Items.list
}
}
const mapDispatchToProps = dispatch => ({
getList: () => dispatch(getList()),
updateList: (list) =>
dispatch(updateList(list))
})
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(Item));
Sages file
import { put, takeLatest, all, call,select } from "redux-saga/effects";
function* fetchNews() {
const json = yield fetch(
"http://petstore.swagger.io/v2/pet/findByStatus?status=available"
).then(response => response.json());
yield put({ type: "GET_LIST_SUCCESS", json: json });
}
function * updateNewList(data){
///console.log(data.payload)
yield put({ type: "GET_LIST_SUCCESS", list: data.payload });
}
function * fetchupateList(){
const signals = yield select(store => store)
console.log(signals)
}
function* actionWatcher() {
yield takeLatest("GET_LIST_REQUEST", fetchNews);
yield takeLatest("GET_UPDATED_LIST_REQUEST", fetchupateList);
yield takeLatest("UPDATE_LIST_REQUEST", updateNewList);
}
export default function* rootSaga() {
yield all([actionWatcher()]);
}
**Second Component **
import React from "react";
import ReactDOM from "react-dom";
import PropTypes from 'prop-types'
import { withRouter } from "react-router-dom";
import { connect } from "react-redux";
import { makeStyles } from "#material-ui/core/styles";
import TextField from "#material-ui/core/TextField";
import Button from "#material-ui/core/Button";
import {getList,updateList,getUpdatedList} from "./../Action/Action";
export class Payment extends React.Component {
constructor(props) {
super(props);
this.state = {
pickedItem: [1, 2]
};
}
componentWillMount() {
this.props.getUpdatedList();
}
render() {
console.log(this.props)
const { pickedItem } = this.state;
//console.log(pickedItem);
return (
<div className="PaymentPage">
<div className="pageWrapper">
<form noValidate autoComplete="off">
<h1>Payment Details</h1>
<TextField
id="outlined-name"
label="Card Type"
margin="normal"
variant="outlined"
/>
<TextField
id="outlined-name"
label="Card Name"
margin="normal"
variant="outlined"
/>
<TextField
id="outlined-name"
label="Card Number"
margin="normal"
variant="outlined"
/>
<div className="clm-2-inp">
<TextField
id="outlined-name"
label="Expiry Date (MM/YYYY)"
margin="normal"
variant="outlined"
/>
<TextField
id="outlined-name"
label="CVV"
margin="normal"
variant="outlined"
/>
</div>
</form>
<div className="itemsection">
<h2>Summery</h2>
<div className="item-list">
<div className="logoWarapper">
<img
src="https://rukminim1.flixcart.com/image/660/792/jmdrr0w0/shirt/q/q/r/xxl-tblwtshirtful-sh4-tripr-original-imaf9ajwb3mfbhmh.jpeg?q=50"
width="100"
height="100"
alt=""
/>
</div>
<div className="itemWarapper">
<h3>Item Name</h3>
<p>
<span>₹</span>
<span>3000</span>
</p>
</div>
</div>
<Button variant="contained" color="primary">
Submit Purchase
</Button>
</div>
</div>
</div>
);
}
}
Payment.propTypes = {
list: PropTypes.object,
getList: PropTypes.func,
updateList:PropTypes.func,
getUpdatedList:PropTypes.func
}
function mapStateToProps(state,ownProps){
console.log(state,ownProps)
const Items= state
return {
list : Items.list
}
}
const mapDispatchToProps = {
getList,
updateList,
getUpdatedList
};
export default withRouter(connect(
mapStateToProps,
mapDispatchToProps
)(Payment));
Reducer
const initialState = {
list: {}
}
const Reducer = (state = initialState, action) => {
switch (action.type) {
case "GET_LIST_SUCCESS":
return {
...state,
list: action.json,
}
case "GET_LIST_SUCCESS":
return {
...state,
list: action.list,
}
default:
return state;
}
};
export default Reducer;
Once i click the "Make payment" button in the first component, i will updated the list with some modification those modified changes i want to get in the second component.
I unable to get first redux store value in the second component.
Help me to ix this issue please.
I used to make this code work out for my search component but after the on submit is called, I receive this error which never happened before, does anyone have any clue???
Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.
import React, { Component } from "react";
import axios from "axios";
import { Redirect } from "react-router-dom";
import { Consumer } from "../context";
class Search extends Component {
constructor() {
super();
this.state = {
productTitle: "",
apiUrl: "*******************************",
redirect: false
};
}
findProduct = (dispatch, e) => {
e.preventDefault();
axios
.post(
`${this.state.apiUrl}`,
JSON.stringify({ query: this.state.productTitle })
)
.then(res => {
dispatch({
type: "SEARCH_TRACKS",
payload: res.data.output.items
});
this.setState({ items: res.data.output.items, redirect: true });
})
.catch(err => console.log(err));
};
onChange = e => {
this.setState({ [e.target.name]: e.target.value });
};
render() {
const { redirect } = this.state;
if (redirect) {
return <Redirect to="/searchresult" />;
}
return (
<Consumer>
{value => {
const { dispatch } = value;
return (
<div>
<form onSubmit={this.findProduct.bind(this, dispatch)}>
<div className="form-group" id="form_div">
<input
type="text"
className="form-control form-control-md"
placeholder="...محصولات دسته یا برند مورد نظرتان را انتخاب کنید"
name="productTitle"
value={this.state.productTitle}
onChange={this.onChange}
/>
<button className="btn" type="submit">
<i className="fas fa-search" />
</button>
</div>
</form>
</div>
);
}}
</Consumer>
);
}
}
import React, { Component } from 'react'
import axios from 'axios'
const Context = React.createContext();
export const axiosDashboard = () => {
const URL = (`*****************`);
return axios(URL, {
method: 'POST',
data: JSON.stringify({refresh:"true"}),
})
.then(response => response.data)
.catch(error => {
throw error;
});
};
const reducer = (state, action) => {
switch(action.type){
case 'SEARCH_TRACKS':
return {
...state,
items: action.payload,
heading: 'Search Results'
};
default:
return state;
}
}
export class Provider extends Component {
state = {
dispatch:action => this.setState(state => reducer(state, action))
}
render() {
return (
<Context.Provider value={this.state}>
{this.props.children}
</Context.Provider>
)
}
}
export const Consumer = Context.Consumer
import React, { Component } from 'react'
import { Consumer } from '../context'
import SearchResult from './SearchResult'
import './Search.css'
class Tracks extends Component {
render() {
return (
<Consumer>
{value => {
const { items } = value
if(items === undefined || items.length === 0){
return 'hello'}
else{
return(
<React.Fragment>
<div id='products_search'>
<div className='container'>
<div className="row justify-content-end">
{items.map(item => (
<SearchResult
key={item.id}
id={item.id}
title={item.name}
current_price={item.current_price}
lowest_price={item.lowest_price}
store_name={item.store_name}
thumb={item.thumb_url}/>
))}
</div>
</div>
</div>
</React.Fragment>
)
}
}}
</Consumer>
)
}
}
export default Tracks
import React from 'react'
import {Link} from 'react-router-dom'
import './Search.css'
const SearchResult = (props) => {
const {title,current_price,lowest_price,thumb,id,store_name} = props
return (
<div className="col-md-3" id="searchresult">
<img src={thumb} alt=""/>
<div className="sexy_line"></div>
<p className="muted">{store_name}</p>
<h6>{title}</h6>
<p>{lowest_price}</p>
<Link to={`products/item/${id}`}>
<button type="button" className="btn btn-light rounded-pill">{
new Intl
.NumberFormat({style: 'currency', currency: 'IRR'})
.format(current_price)
}</button>
</Link>
</div>
)
}
export default SearchResult
Maximum update depth exceeded.
This means that you are in a infinit loop of re rendering a component.
The only place where I can see this is possible to happen is in this part
if (redirect) {
return <Redirect to="/searchresult" />;
}
Maybe you are redirecing to the a route that will get the same component that have the redirect.
Please check if you aren't redirecting to the same route as this component and provide your routes and what is inside Consumer.
I have the problem when I use the Reactjs, I'm really new to Reactjs, so maybe it's a easy problem
I want to use the class ClickButton in the UserInfo,but I don't know how to change the name through props
import React, { PropTypes } from 'react';
import { Button } from 'antd';
import { connect } from 'react-redux';
import styles from './ClickButton.less';
const ClickButton = ({ todos,dispatch }) => {
const userinforclick = () => {
dispatch({
type: 'todos/clickbutton',
payload: !todos['click_button'],
});
};
return (
<span>
< span type="primary" className={ styles.show } onClick={ userinforclick.bind(this) } > {this.props.name} < /span >
</span>
);
};
function clickbutton({ todos }){
return{
todos:todos,
}
}
export default connect(clickbutton)(ClickButton)
and i use the ClickButton in UserInfo:
import React from 'react'
import styles from './Userinfo.less'
import ClickButton from '../../components/Button/ClickButton'
import { connect } from 'react-redux';
import { Spin } from 'antd'
const Userinfo = ({ todos,dispatch }) => {
const { userinfo, userinfoloading, click_button } = todos;
if(userinfoloading) {
return <Spin />;
}
const renderList = () => {
return(
<div className={ styles.userInfodiv}>
<div>
<span className={ styles.userInfoTitle }>userinfo</span>
</div>
<div className = { styles.slice }></div>
<div className = { styles.userInfoBody}>
<div className = { styles.userInfoSubBody }>
<span>username:</span>
<span>{userinfo[0]['username']}</span>
</div>
<div className = { styles.userInfoSubBody }>
<span>number:</span>
{ click_button ? <span>{userinfo[0]['phone']}</span> : <input type="text" value={userinfo[0]['phone']} /> }
<ClickButton name="john" />
</div>
</div>
</div>
);
};
return (
<div>
{ renderList() }
</div>
);
};
function mapStateToProps({ todos }) {
return {
todos: todos,
};
}
export default connect(mapStateToProps)(Userinfo);
Here's something that actually works (although I removed the todos and such but you can add them in easily):
class RenderList extends React.Component {
render() {
return (<span> {this.props.name} </span>);
}
}
class App extends React.Component {
render() {
return (<div>
<RenderList name="John"/>
</div>)
}
}
ReactDOM.render(<App/>,document.getElementById("app"));