I'm trying to achieve making the user log out after the countdown ends, I'm sorry because I am just new in react still learning, anyways this is my code. Thank you so much for your help I really appreciate it. Has an error that says TypeError: this.props.onLogout is not a function :)
My Code:
import React, { Component } from 'react';
import Countdown from "react-countdown";
import {
Redirect,
} from 'react-router-dom';
import { logoutUser } from '../../../actions/authActions';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
export class OnlineOrders extends Component {
onLogout = (e) => {
e.preventDefault();
this.props.logoutUser();
};
render() {
const renderer = ({ days, hours, minutes, seconds, completed }) => {
if (completed) {
this.props.onLogout();
return <>
<Redirect to='/billing/plans' />
</>;
} else {
return (
<span>
{days}:{hours}:{minutes}:{seconds}
</span>
);
}
};
return (
<>
<div className='note primary'>
You have <strong><Countdown date={Date.now() + 5000} renderer={renderer} /></strong> remaining on your free trial.
<a href='/billing/plans'> Activate Now</a> to stay alive!
</div>
</>
);
};
}
OnlineOrders.propTypes = {
logoutUser: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
auth: state.auth,
});
export default connect(mapStateToProps, { logoutUser })(OnlineOrders);
Looks like you need to invoke onLogout correctly.
if (completed) {
this.onLogout();
return <Redirect to='/billing/plans' />;
} else {
return (
<span>
{days}:{hours}:{minutes}:{seconds}
</span>
);
}
Side note: You can invoke the callback from props directly and save a function declaration.
if (completed) {
this.props.logoutUser();
return <Redirect to='/billing/plans' />;
} else {
return (
<span>
{days}:{hours}:{minutes}:{seconds}
</span>
);
}
The else is actually also superfluous, it can be removed.
const renderer = ({ days, hours, minutes, seconds, completed }) => {
if (completed) {
this.props.logoutUser();
return <Redirect to='/billing/plans' />;
}
return (
<span>
{days}:{hours}:{minutes}:{seconds}
</span>
);
};
Related
I'm using React redux to build a div of a number that you can raise or to subtract.
enter image description here
the problem is that everything seems to look good
but there is no result on the browser
reducer-> index.js:
import { combineReducers } from 'redux'
const sumReducer = (sum = 0, action) => {
if (action.type === 'ADD' || action.type === 'SUB') {
let x = action.payload;
return x;
}
return sum;
}
export default combineReducers({
sum: sumReducer
});
**action-> index.js**
export const add = (sum) => {
return {
type: 'ADD',
payload: sum + 1
}
}
export const sub = (sum) => {
return {
type: 'SUB',
payload: sum - 1
}
}
my component is: upDownSum.js
import React from 'react'
import { connect } from 'react-redux'
import { add, sub } from '../action'
class UpDownSum extends React.Component {
render() {
console.log(this.props);
console.log("ghj");
return (
<div key="1">
<button onClick={() => sub(this.props.sum)}>-</button>
<div>{this.props.sum}</div>
<button onClick={() => add(this.props.sum)}>+</button>
</div>
)
}
}
const MapStateToProps = (state) => {
return {
sum: state.sum
}
}
export default connect(MapStateToProps, {add: add ,sub: sub})(UpDownSum);
app.js
import './App.css';
import UpDownSum from './upDownSum'
function App() {
return (
<UpDownSum/>
);
}
export default App;
Thank you!
You are mapping dispatch to props, meaning to dispatch actions you will need to make sure to use props and not just the imported actions:
return (
<div>
<button onClick={() => this.props.sub(this.props.sum)}>-</button>
<div>{this.props.sum}</div>
<button onClick={() => this.props.add(this.props.sum)}>+</button>
</div>
)
I ported my application to redux-thunk and the error started to appear in the console(
mapDispatchToProps() in Connect(UsersContainer) must return a plain object. Instead received undefined.).
But with the appearance of this error, nothing has changed. How to fix it?
Reducer:
import {getTeamApi} from "./api";
let date = {
teamDate: []
};
const realtorsDate = (state = date, action) => {
switch (action.type) {
case "GetTeam":
return {...state, teamDate: [...state.teamDate, ...action.team]};
default:
return state
}
}
export let GetTeam = (team) => ({
type: "GetTeam",
team
})
export default realtorsDate;
export const getTeamThunks = () => {
return (dispatch) => {
getTeamApi.then(response => {
dispatch(GetTeam(response.items));
});
}
}
Container component:
import {connect} from "react-redux";
import {getTeamThunks} from "../../store/realtorsDate";
import ScrollableAnchor from "react-scrollable-anchor";
import MainFourth from "./main-fourth";
import Photo from "../../Images/pivo-3.jpg";
import React from "react";
class UsersContainer extends React.Component {
render() {
return (
<section>
<ScrollableAnchor id={"team"}>
<h2>Наша команда</h2>
</ScrollableAnchor>
<div className="MainFourth">
{this.props.realtorsDate.map((el, i) => (
<MainFourth key={i} el={el} Photo={Photo}></MainFourth>))}
</div>
</section>
);
}
}
let MapStateToProps = (state) => {
return {
realtorsDate: state.realtorsDate.teamDate
}
}
let FourthContainerBlock = connect(MapStateToProps, getTeamThunks)(UsersContainer)
export default FourthContainerBlock
Component:
import React from "react";
import "./../../css/App.css";
import {FontAwesomeIcon} from "#fortawesome/react-fontawesome";
import {faAt, faMobileAlt} from "#fortawesome/free-solid-svg-icons";
class MainFourth extends React.Component {
render() {
return (
<div className="team" key={this.props.i}>
<div className="about-team">
<h2 className="team-name">
{this.props.el.SecondName}
{this.props.el.Name}
</h2>
<h2 className="team-position">{this.props.el.Position}</h2>
<p><FontAwesomeIcon icon={faMobileAlt}></FontAwesomeIcon> : {this.props.el.Phone}</p>
<p><FontAwesomeIcon icon={faAt}></FontAwesomeIcon> : {this.props.el.Mail}</p>
</div>
<img className="TeamsPhoto" src={this.props.Photo} alt="" />
</div>
);
}
}
export default MainFourth;
Ty all
The error is actually pretty straight forward, getTeamThunks should return a js object and that should be synchronous. You can do asynchronous things in the action creators.
The second argument to connect is simply used to map the dispatch to props(the reason why most tend to name it as such).
For example, mapDispatchToProps can look like this:
const mapDispatchToProps = (dispatch) => {
return {
getTeamThunks: () => dispatch(actions.getTeamThunksData())
}
}
let FourthContainerBlock = connect(MapStateToProps, mapDispatchToProps)(UsersContainer)
Now the getTeamthunksData can be written like this:
export const getTeamThunksData = () => {
return (dispatch) => {
getTeamApi.then(response => {
dispatch(GetTeam(response.items));
});
}
}
In your component, you can dispatch it by using this.props.getTeamThunks(). Where you execute it depends on your requirements.
I am using react-redux.
I have the following JSX (only relevant snippets included):
getQuestionElement(question) {
if (question) {
return <MultiChoice questionContent={this.props.question.question} buttonClicked={this.choiceClicked} />
}
else {
return (
<div className="center-loader">
<Preloader size='big' />
</div>
)
}
}
render() {
return (
<div>
<Header />
{
this.getQuestionElement(this.props.question)
}
</div>
)
}
function mapStateToProps({ question }) {
return { question };
}
export default connect(mapStateToProps, questionAction)(App);
When the action fires, and the reducer updates the question prop
this.props.question
I expect
{this.getQuestionElement(this.props.question)}
to be reloaded and the new question rendered.
However this is not happening. Am I not able to put a function in this way to get it live reloaded?
My MultiChoice component:
import React, { Component } from 'react';
import ReactHtmlParser from 'react-html-parser';
import './questions.css';
class MultiChoice extends Component {
constructor(props) {
super(props);
this.state = {
question: this.props.questionContent.question,
answerArray : this.props.questionContent.answers,
information: null
}
this.buttonClick = this.buttonClick.bind(this);
}
createButtons(answerArray) {
var buttons = answerArray.map((element) =>
<span key={element._id} onClick={() => { this.buttonClick(element._id) }}
className={"span-button-wrapper-25 " + (element.active ? "active" : "")}>
<label>
<span>{element.answer}</span>
</label>
</span>
);
return buttons;
}
buttonClick(id) {
var informationElement;
this.props.buttonClicked(id);
var buttonArray = this.state.answerArray.map((element) => {
if (element._id === id ){
element.active = true;
informationElement = element.information;
return element;
}
else{
element.active = false;
return element;
}
});
this.setState({
answerArray: buttonArray,
information: informationElement
})
}
render() {
return (
<div className="question-container">
<div className="question-view">
<div className="icon-row">
<i className="fa fa-code" />
</div>
<div className="title-row">
{this.state.question}
</div>
<div className="button-row">
{this.createButtons(this.state.answerArray)}
</div>
<div className="information-row">
{ReactHtmlParser(this.state.information)}
</div>
</div>
</div>
);
}
}
export default MultiChoice;
QuestionAction.js
import axios from "axios";
import { FETCH_QUESTION } from "./types";
export const fetchQuestion = (questionId, answerId) => async dispatch => {
let question = null;
if (questionId){
question = await axios.get("/api/question/next?questionId=" + questionId + "&answerId=" + answerId);
}
else{
question = await axios.get("/api/question/next");
}
console.log("question", question);
dispatch({ type: FETCH_QUESTION, payload: question });
};
questionReducer.js
import {FETCH_QUESTION } from "../actions/types";
export default function(state = null, action) {
switch (action.type) {
case FETCH_QUESTION:
console.log("payload", action.payload.data);
return { question: action.payload.data, selected: false };
default:
return state;
}
}
index.js (Combined Reducer)
import { combineReducers } from 'redux';
import questionReducer from './questionReducer';
export default combineReducers({
question: questionReducer
});
and my entry point:
index.js
const store = createStore(reducers, {}, applyMiddleware(reduxThunk));
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
);
registerServiceWorker();
requested console.log response:
render() {
console.log("Stackoverflow:", this.props.question)
.....
and after clicking the button (and the reducer updating, the console.log is updated, but the
this.getQuestionElement(this.props.question)
does not get re-rendered
MultiChoice Component shouldn't store his props in his state in the constructor, you have 2 options here :
Handle props changes in componentWillReceiveProps to update the state :
class MultiChoice extends Component {
constructor(props) {
super(props);
this.state = {
question: this.props.questionContent.question,
answerArray : this.props.questionContent.answers,
information: null
}
this.buttonClick = this.buttonClick.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({
question: nextProps.questionContent.question,
answerArray : nextProps.questionContent.answers,
information: null
});
}
We have to keep using the constructor to set an initial state as from docs :
React doesn’t call componentWillReceiveProps() with initial props
during mounting.
2nd Option : Make it as a "dumb component" by having no state and only using his props to render something (some more deep changes in your component to do, especially to handle the "active" element, it will have to be handled by the parent component).
The this.props does not have the loading, error key.
Why?
My code on :
https://github.com/jiexishede/react-redux-demo01
You can fork it and pull request.
Because you don't pass them in your mapStateToProps function
https://github.com/jiexishede/react-redux-demo01/blob/0c1407935cd6c461705d6ca37f3e33484afac327/src/views/Home.js#L8-L10
This should be something like:
#connect(state => {
return {
articleList: state.home.list.articleList,
loading: state.home.list.loading,
error: state.home.list.error,
};
You didn't set up your component to receive updates from your store. Your component won't know that the reducer has updated the state. Check out the code below:
import React, { Component } from 'react';
import * as Redux from 'react-redux'; // Import redux
import Preview from './Preview'
class PreviewList extends Component {
static propTypes = {
loading:React.PropTypes.bool, // 注意 bushi PropTypes.bool, 前面要价 React
error:React.PropTypes.bool,
articleList: React.PropTypes.arrayOf(React.PropTypes.object),
loadArticles: React.PropTypes.func
};
componentDidMount(){
this.props.loadArticles();
}
render(){
const { loading, error, articleList } = this.props;
if(error){
return <p className="message">)0ops, something is wrong. </p>
}
if(loading){
return <p className="message">Loading....</p>
}
// return this.props.articleList.map(item => (
// <Preview {...item} key={item.id}/>
// ))
return (
<div>
{articleList.map(item => {
return <Preview {...item} key={item.id} push={this.props.push} />
})}
</div>
);
}
}
// Connect your component to your store and
// receive updates from your previewList reducer:
export default Redux.connect(state => {
return {
loading: state.previewList.loading,
error: state.previewList.error,
articleList: state.previewList.articleList
};
})(PreviewList);
The problem is that you're not connecting your component to the redux store. You need to install the react-redux package then use it's connect function to connect your component to the store like the following:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Preview from './Preview';
import { loadArticles } from './PreviewListRedux';
class PreviewList extends Component {
static propTypes = {
loading:React.PropTypes.bool,
error:React.PropTypes.bool,
articleList: React.PropTypes.arrayOf(React.PropTypes.object),
loadArticles: React.PropTypes.func
};
componentDidMount(){
this.props.loadArticles();
}
render(){
if (!this.props.loading) {
return <div>Loading...</div>
}
const { loading, error, articleList } = this.props;
if(error){
return <p className="message">)0ops, something is wrong. </p>
}
if(loading){
return <p className="message">Loading....</p>
}
return (
<div>
{articleList.map(item => {
return <Preview {...item} key={item.id} push={this.props.push} />
})}
</div>
);
}
}
const mapStateToProps = (state) => {
return {
loading: state.list.loading,
error: state.list.error,
articleList: state.list.articleList
}
};
export default connect(mapStateToProps, { loadArticles })(PreviewList);
Also, your code needs some major restructuring, it's really difficult to read through it and see how the different pieces are connected together.
I am slowly learning React and also learning to implement it with Redux. But I seem to have hit a road block. So this is what I have so far.
/index.jsx
import './main.css'
import React from 'react'
import ReactDOM from 'react-dom'
import App from './components/App.jsx'
import { Provider } from 'react-redux'
import { createStore } from 'redux'
import ShoppingList from './reducers/reducer'
let store = createStore(ShoppingList)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('app')
)
/actions/items.js
import uuid from 'node-uuid'
export const CREATE_ITEM = 'CREATE_ITEM'
export function createItem(item) {
return {
type: CREATE_ITEM,
item: {
id: uuid.v4(),
item,
checked: false
}
}
}
/reducers/reducer.js
import * as types from '../actions/items'
import uuid from 'node-uuid'
const initialState = []
const items = (state = initialState, action) => {
switch (action.type) {
case types.CREATE_ITEM:
return {
id: uuid.v4(),
...item
}
default:
return state;
}
}
export default items
/reducers/index.js
UPDATE:
import { combineReducers } from 'redux'
import items from './reducer'
const ShoppingList = combineReducers({
items
})
export default ShoppingList
/components/Item.jsx
import React from 'react';
import uuid from 'node-uuid'
export default class Item extends React.Component {
constructor(props) {
super(props);
this.state = {
isEditing: false
}
}
render() {
if(this.state.isEditing) {
return this.renderEdit();
}
return this.renderItem();
}
renderEdit = () => {
return (
<input type="text"
ref={(event) =>
(event ? event.selectionStart = this.props.text.length : null)
}
autoFocus={true}
defaultValue={this.props.text}
onBlur={this.finishEdit}
onKeyPress={this.checkEnter}
/>
)
};
renderDelete = () => {
return <button onClick={this.props.onDelete}>x</button>;
};
renderItem = () => {
const onDelete = this.props.onDelete;
return (
<div onClick={this.edit}>
<span>{this.props.text}</span>
{onDelete ? this.renderDelete() : null }
</div>
);
};
edit = () => {
this.setState({
isEditing: true
});
};
checkEnter = (e) => {
if(e.key === 'Enter') {
this.finishEdit(e);
}
};
finishEdit = (e) => {
const value = e.target.value;
if(this.props.onEdit) {
this.props.onEdit(value);
this.setState({
isEditing: false
});
}
};
}
/components/Items.jsx
import React from 'react';
import Item from './Item.jsx';
export default ({items, onEdit, onDelete}) => {
return (
<ul>{items.map(item =>
<li key={item.id}>
<Item
text={item.text}
onEdit={onEdit.bind(null, item.id)}
onDelete={onDelete.bind(null, item.id)}
/>
</li>
)}</ul>
);
}
// UPDATE: http://redux.js.org/docs/basics/UsageWithReact.html
// Is this necessary?
const mapStateToProps = (state) => {
return {
state
}
}
Items = connect(
mapStateToPros
)(Items) // `SyntaxError app/components/Items.jsx: "Items" is read-only`
//////////////////////////////////////
// Also tried it this way.
//////////////////////////////////////
Items = connect()(Items)
export default Items // same error as above.
Tried this as well
export default connect(
state => ({
items: store.items
})
)(Items) // `Uncaught TypeError: Cannot read property 'items' of undefined`
UPDATE:
After many attempts #hedgerh in Gitter pointed out that it should be state.items instead. so the solution was
export default connect(
state => ({
items: state.items
})
)(Items)
credits to #azium as well.
/components/App.jsx
export default class App extends React.Component {
render() {
return (
<div>
<button onClick={this.addItem}>+</button>
<Items />
</div>
);
}
}
What am I missing here in order to implement it correctly? Right now it breaks saying that Uncaught TypeError: Cannot read property 'map' of undefined in Items.jsx. I guess it makes sense since it doesn't seem to be hooked up correctly. This is the first part of the app, where the second will allow an user to create a many lists, and these lists having many items. I will probably have to extract the methods from Item.jsx since the List.jsx will do pretty much the same thing. Thanks
You're missing connect. That's how stuff gets from your store to your components. Read the containers section from the docs http://redux.js.org/docs/basics/UsageWithReact.html
import React from 'react'
import Item from './Item.jsx'
import { connect } from 'react-redux'
let Items = ({items, onEdit, onDelete}) => {
return (
<ul>{items.map(item =>
<li key={item.id}>
<Item
text={item.text}
onEdit={onEdit.bind(null, item.id)}
onDelete={onDelete.bind(null, item.id)}
/>
</li>
})
</ul>
)
}
export default connect(
state => ({
items: state.items
})
)(Items)
Also you seem to be expecting onEdit and onDelete functions passed from a parent but you're not doing that so those functions will be undefined.