I have a problem when want to show
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
userId: state.auth.userId,
tokenId: state.auth.token
}
}
this in my function
register = (event) => {
event.preventDefault()
this.props.onAuth( this.state.email, this.state.password, this.state.isSignup );
localStorage.setItem('token', this.props.tokenId);
localStorage.setItem('userId', this.props.userId);
}
I see token and userId after the second click. But I can't see after the first click. What I need more to show immediately?
This is my auth.js reducers
import * as actionTypes from '../actions/actionsTypes';
import { updateObject } from '../utility';
const initialState = {
token: null,
userId: null,
error: null,
loading: false
};
const authStart = ( state, action ) => {
return updateObject( state, { error: null, loading: true } );
};
const authSuccess = (state, action) => {
return updateObject( state, {
token: action.idToken,
userId: action.userId,
error: null,
loading: false
} );
};
const authFail = (state, action) => {
return updateObject( state, {
error: action.error,
loading: false
});
}
const reducer = ( state = initialState, action ) => {
switch ( action.type ) {
case actionTypes.AUTH_START: return authStart(state, action);
case actionTypes.AUTH_SUCCESS: return authSuccess(state, action);
case actionTypes.AUTH_FAIL: return authFail(state, action);
default:
return state;
}
};
export default reducer;
But, after the first click, I got token in my render function.
{this.props.tokenId}
Could you please help me? I think I need to use async/await. But I am not sure.
Here you go Header.js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import * as actions from '../../store/actions/index'
import PropTypes from 'prop-types'
import './header.css'
class Header extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
isSignup: true,
token: false
}
this.handleChange = this.handleChange.bind(this);
}
handleChange (evt) {
this.setState({ [evt.target.name]: evt.target.value });
}
switchAuthModeHandler = (event) => {
event.preventDefault()
this.setState(prevState => {
return {
isSignup: !prevState.isSignup
}
})
}
register = (event) => {
event.preventDefault()
this.props.onAuth( this.state.email, this.state.password, this.state.isSignup );
localStorage.setItem('token', this.props.tokenId);
localStorage.setItem('userId', this.props.userId);
}
render() {
let regBtn = ''
if (this.state.isSignup) {
regBtn = 'Register'
}
else {
regBtn = 'Login'
}
let login = null
if(!this.props.tokenId) {
login = (
<div className="login">
<form onSubmit={this.register}>
<input type="email" placeholder="email" name="email" onChange={this.handleChange} />
<input type="password" placeholder="password" name="password" onChange={this.handleChange} />
<button>{regBtn}</button>
</form>
<div onClick={this.switchAuthModeHandler} className="switch">Switch to {this.state.isSignup ? 'Login' : 'Register'}</div>
</div>
)
}
else {
login = (
<div>
<p>Hello: {this.props.userId}</p>
<button>Logout</button>
</div>
)
}
if(this.props.loading) {
login = <div>Loading...</div>
}
return (
<div>
<div className="header-inner">
{this.props.tokenId}
{login}
<img src="http://civcic.com/assets/images/header-bg.jpg" alt="img" />
<div className="header-content">
<h2>React.JS DEVELOPER</h2>
<a className="knowmore-btn" href="https://www.upwork.com/freelancers/~01f507600be26cc2a3" rel="noopener noreferrer" target="_blank">Upwork profile</a><br />
<a className="knowmore-btn" href="https://www.linkedin.com/in/boris-civcic-37244378/" rel="noopener noreferrer" target="_blank">Linkedin</a><br />
<a className="knowmore-btn" href="https://github.com/fixman93" rel="noopener noreferrer" target="_blank">GitHub</a>
</div>
</div>
</div>
)
}
}
Header.defaultProps = {
tokenId: ''
}
Header.propTypes = {
tokenId: PropTypes.string
}
const mapStateToProps = state => {
return {
loading: state.auth.loading,
error: state.auth.error,
userId: state.auth.userId,
tokenId: state.auth.token
}
}
const mapDispatchToProps = dispatch => {
return {
onAuth: ( email, password, isSignup) => dispatch( actions.auth(email, password, isSignup))
}
}
export default connect(mapStateToProps, mapDispatchToProps)(Header)
import axios from 'axios';
import * as actionTypes from './actionsTypes';
export const authStart = () => {
return {
type: actionTypes.AUTH_START
}
}
export const authSuccess = (token, userId) => {
return {
type: actionTypes.AUTH_SUCCESS,
idToken: token,
userId: userId
}
}
export const authFail = (error) => {
return {
type: actionTypes.AUTH_FAIL,
error: error
};
};
export const auth = (email, password, isSignup) => {
return dispatch => {
dispatch(authStart());
const authData = {
email: email,
password: password,
fullName: 'Boris Civcic',
role: 'admin',
returnSecureToken: true
};
let url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser?key=AIzaSyC5nW8-XOJADEvU7Mi7sgmhUNhHfRxXNQI';
if (!isSignup) {
url = 'https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyPassword?key=AIzaSyC5nW8-XOJADEvU7Mi7sgmhUNhHfRxXNQI';
}
axios.post(url, authData)
.then(response => {
console.log(response);
dispatch(authSuccess(response.data.idToken, response.data.localId));
// dispatch(checkAuthTime(response.data.expiresIn));
})
.catch(err => {
dispatch(authFail(err.response.data.error));
})
};
};
this is auth.js action
and this is utility
export const updateObject = (oldObject, updatedProperties) => {
return {
...oldObject,
...updatedProperties
};
};
In your register handler, onAuth is an asynchronous action but you've populated localStorage immediately. You should wait onAuth returns and then set your localStorage items.
first return a promise from your thunk ( simply by adding return before axios ):
...
return axios.post(url, authData)
.then(response => {
console.log(response);
dispatch(authSuccess(response.data.idToken, response.data.localId));
// dispatch(checkAuthTime(response.data.expiresIn));
})
.catch(err => {
dispatch(authFail(err.response.data.error));
})
...
Then set your localStorage items like this:
register = (event) => {
event.preventDefault();
this.props.onAuth( this.state.email, this.state.password, this.state.isSignup )
.then(() => {
localStorage.setItem('token', this.props.tokenId);
localStorage.setItem('userId', this.props.userId);
});
}
Related
I have a reusable component for Sign in with Apple Button
After user success, i navigate hem to Home screen
But i notes when i log navigation it's log undefined,
and when i log this.props i just got the two actions i made in redux!
So how can i access to navigation in this component and why it's not accessed by default!
Log
props => {"isLogin": [Function isLogin], "storeToken": [Function storeToken]}
navigation => undefined
Code
import appleAuth, {
AppleAuthCredentialState,
AppleAuthError,
AppleAuthRealUserStatus,
AppleAuthRequestOperation,
AppleAuthRequestScope,
AppleButton,
} from '#invertase/react-native-apple-authentication';
import React from 'react';
import {ActivityIndicator, StyleSheet, View} from 'react-native';
import {connect} from 'react-redux';
import API from '../../api/API';
import {isLoginFunc} from '../../redux/actions/isLoginAction';
import {saveToken} from '../../redux/actions/saveTokenAction';
class AppleAuth extends React.Component {
constructor(props) {
super(props);
this.authCredentialListener = null;
this.user = null;
this.state = {
credentialStateForUser: -1,
loading: false,
};
}
componentDidMount() {
const {navigation} = this.props;
console.log('did-navigation', navigation);
console.log('did- this.props', this.props);
/**
* subscribe to credential updates.This returns a function which can be used to remove the event listener
* when the component unmounts.
*/
this.authCredentialListener = appleAuth.onCredentialRevoked(async () => {
// console.warn('Credential Revoked');
this.fetchAndUpdateCredentialState().catch(error =>
this.setState({credentialStateForUser: `Error: ${error.code}`}),
);
});
this.fetchAndUpdateCredentialState()
.then(res => this.setState({credentialStateForUser: res}))
.catch(error =>
this.setState({credentialStateForUser: `Error: ${error.code}`}),
);
}
componentWillUnmount() {
/**
* cleans up event listener
*/
this.authCredentialListener();
}
signIn = async () => {
// start a login request
try {
const appleAuthRequestResponse = await appleAuth.performRequest({
requestedOperation: AppleAuthRequestOperation.LOGIN,
requestedScopes: [
AppleAuthRequestScope.EMAIL,
AppleAuthRequestScope.FULL_NAME,
],
});
this.setState({loading: true});
const {
user: newUser,
email,
nonce,
fullName: {familyName, givenName},
identityToken,
realUserStatus /* etc */,
} = appleAuthRequestResponse;
let username = `${givenName} ${familyName}`;
this.user = newUser;
this.fetchAndUpdateCredentialState()
.then(res => {
this.setState({credentialStateForUser: res});
console.log('res:::', res);
})
.catch(error => {
console.log(`Error: ${error.code}`);
this.setState({credentialStateForUser: `Error: ${error.code}`});
});
if (identityToken) {
console.log('email', email);
console.log('username', username);
console.log('nonce', nonce);
this.sendData(email, username, nonce);
// e.g. sign in with Firebase Auth using `nonce` & `identityToken`
} else {
// no token - failed sign-in?
}
if (realUserStatus === AppleAuthRealUserStatus.LIKELY_REAL) {
console.log("I'm a real person!");
}
// console.warn(`Apple Authentication Completed, ${this.user}, ${email}`);
} catch (error) {
if (error.code === AppleAuthError.CANCELED) {
alert('User canceled Apple Sign in');
// console.warn('User canceled Apple Sign in.');
} else {
console.error(error);
}
}
};
fetchAndUpdateCredentialState = async () => {
if (this.user === null) {
this.setState({credentialStateForUser: 'N/A'});
} else {
const credentialState = await appleAuth.getCredentialStateForUser(
this.user,
);
if (credentialState === AppleAuthCredentialState.AUTHORIZED) {
this.setState({credentialStateForUser: 'AUTHORIZED'});
} else {
this.setState({credentialStateForUser: credentialState});
}
}
};
// Send data "name,image,email" to API
sendData = async (Email, Name, Id) => {
try {
let response = await API.post('/apple', {
email: Email,
name: Name,
id: Id,
});
let {
data: {
data: {
response: {token},
},
},
} = response;
console.log('token:?>:', token);
console.log('props', this.props);
console.log('navigation', this.props.navigation);
this.setState({loading: false});
this.props.storeToken(token);
this.props.isLogin(true);
// this.props.navigation.push('BottomTabNavigator');
} catch (err) {
console.log(err);
alert('Unexpected Error, try again later.');
this.setState({loading: false});
}
};
render() {
return (
<View style={styles.container}>
{this.state.loading ? (
<ActivityIndicator />
) : (
<AppleButton
style={styles.appleButton}
cornerRadius={5}
buttonStyle={AppleButton.Style.WHITE}
buttonType={AppleButton.Type.SIGN_IN}
onPress={() => this.signIn()}
/>
)}
</View>
);
}
}
const styles = StyleSheet.create({
appleButton: {
width: 200,
height: 50,
// margin: 10,
},
container: {
flex: 1,
justifyContent: 'center',
},
});
const mapDispatchToProps = dispatch => {
// to excute the actions we want to invok
return {
isLogin: isLogin => {
dispatch(isLoginFunc(isLogin));
},
storeToken: token => {
dispatch(saveToken(token));
},
};
};
export default connect(
null,
mapDispatchToProps,
)(AppleAuth);
-
singin.js
<AppleAuth /> in the render method
if you render your component as component, not as a navigation screen, it will not receive navigation prop. It was like this in all versions of react-navigation
Access the navigation prop from any component
I am building a social media application using the MERN stack and Redux as the state manager. I have a feed component which renders PostItem components which display the post and allow for actions such as liking, and commenting. I also have a Post component that renders the same PostItem component that opens when the user clicks the comment button on the PostItem component in the feed. When I like a post via the feed component it receives the updated props and rerenders the component showing the changes. However when I click the like button in the Post component it updates the Redux store but does not receive the updated props.
Feed.js
import React, { Fragment, useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getPosts } from '../../actions/post';
// Components
import UserProfileCard from './UserProfileCard';
import PostForm from './PostForm';
import Footer from '../layout/Footer';
import PostItem from '../posts/PostItem';
import Spinner from '../layout/Spinner';
const Feed = ({ getPosts, post: { posts, loading } }) => {
//Same as component did mount
useEffect(() => {
getPosts();
}, [getPosts]);
return (
<Fragment>
<div className='main-container mt-3'>
<div className='container'>
<div className='row'>
<UserProfileCard />
<PostForm />
</div>
{loading ? (
<Spinner />
) : (
posts.map(post => <PostItem key={post._id} post={post} />)
)}
</div>
</div>
<Footer />
</Fragment>
);
};
Feed.propTypes = {
getPosts: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
post: state.post
});
export default connect(mapStateToProps, { getPosts })(Feed);
Post.js
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { connect } from 'react-redux';
import { getPost } from '../../actions/post';
import { Link } from 'react-router-dom';
// Components
import Spinner from '../layout/Spinner';
import PostItem from './PostItem';
import PostCommentForm from './PostCommentForm';
import Comment from './Comment';
// Assets
import { ArrowLeft } from 'react-feather';
const Post = ({ getPost, post: { post, loading }, match }) => {
useEffect(() => {
// get id from url in params for getPost function
getPost(match.params.id);
}, [getPost]);
return loading || post === null ? (
<Spinner />
) : (
<div className='main-container mt-3'>
<div className='container'>
<div className='row'>
{/* TODO ADD BROWSER HISTORY FUNCTIONALITY TO ALLOW USER TO GO BACK TO PROFILE OR FEED */}
<Link className='mb-1' to='/feed'>
<button className='btn btn-logo-color'>
<ArrowLeft />
</button>
</Link>
<PostItem key={post._id} post={post} />
<PostCommentForm />
</div>
<Comment />
</div>
</div>
);
};
Post.propTypes = {
getPost: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
};
const mapStateToProps = state => ({
post: state.post
});
export default connect(mapStateToProps, { getPost })(Post);
PostItem.js
import React from 'react';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import Moment from 'react-moment';
import { connect } from 'react-redux';
import { addLike, removeLike, deletePost } from '../../actions/post';
// Assets
import { ThumbsUp, ThumbsDown, MessageSquare, XCircle } from 'react-feather';
import avi from '../assets/default-avatar.png';
const PostItem = ({
auth,
post: { _id, text, firstname, lastname, user, likes, comments, date },
addLike,
removeLike,
deletePost
}) => {
return (
<div className='card mt-1 post'>
<div className='card-body'>
<div className='row'>
<div className='col-lg-2'>
<Link className='feed-link' to={`/profile/${user}`}>
<img src={avi} alt='avatar' className='avatar' />
<h5 className='card-title mt-2'>
{firstname} {lastname}
</h5>
</Link>
</div>
<div className='col-lg-10'>
<p className='card-text'>{text}</p>
<p className='text-muted post-date'>
<Moment format='LLL'>{date}</Moment>
</p>
<div className='post-buttons'>
<button
type='button'
className='btn btn-outline-primary mr-1'
onClick={e => addLike(_id)}
>
<ThumbsUp />
<span className='badge badge-light'>
{likes.length > 0 && <span>{likes.length}</span>}
</span>
</button>
<button
type='button'
className='btn btn-outline-danger mr-1'
onClick={e => removeLike(_id)}
>
<ThumbsDown />
</button>
{/* TODO ADD CONDITIONAL RENDERING TO REMOVE WHEN POST IS OPEN */}
<Link to={`/post/${_id}`}>
<button type='button' className='btn btn-outline-info mr-1'>
<MessageSquare />
<span className='badge badge-light'>
{comments.length > 0 && (
<span className='comment-count'>{comments.length}</span>
)}
</span>
</button>
</Link>
{!auth.loading && user === auth.user._id && (
<button
type='button'
className='btn btn-outline-danger mr-1'
onClick={() => deletePost(_id)}
>
<XCircle />
</button>
)}
</div>
</div>
</div>
</div>
</div>
);
};
PostItem.propTypes = {
post: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
addLike: PropTypes.func.isRequired,
removeLike: PropTypes.func.isRequired,
deletePost: PropTypes.func.isRequired
};
const mapStateToProps = state => ({
auth: state.auth
});
export default connect(mapStateToProps, { addLike, removeLike, deletePost })(
PostItem
);
Post Reducer
import {
GET_POSTS,
POST_ERROR,
UPDATE_LIKES,
DELETE_POST,
ADD_POST,
GET_POST,
ADD_COMMENT,
REMOVE_COMMENT
} from '../actions/types';
const initialState = {
posts: [],
post: null,
loading: true,
error: {}
};
export default function(state = initialState, action) {
const { type, payload } = action;
switch (type) {
case GET_POSTS:
return {
...state,
posts: payload,
loading: false
};
case GET_POST:
return {
...state,
post: payload,
loading: false
};
case ADD_POST:
return {
...state,
posts: [payload, ...state.posts],
loading: false
};
case DELETE_POST:
return {
...state,
posts: state.posts.filter(post => post._id !== payload),
loading: false
};
case POST_ERROR:
return {
...state,
error: payload,
loading: false
};
case UPDATE_LIKES:
return {
...state,
posts: state.posts.map(post =>
post._id === payload.postId ? { ...post, likes: payload.likes } : post
),
loading: false
};
case ADD_COMMENT:
return {
...state,
post: { ...state.post, comments: payload },
loading: false
};
case REMOVE_COMMENT:
return {
...state,
post: {
...state.post,
comments: state.post.comments.filter(
comment => comment._id !== payload
)
},
loading: false
};
default:
return {
...state
};
}
}
Post actions
import axios from 'axios';
import { setAlert } from './alert';
import {
GET_POSTS,
POST_ERROR,
UPDATE_LIKES,
DELETE_POST,
ADD_POST,
GET_POST,
ADD_COMMENT,
REMOVE_COMMENT
} from './types';
//Get posts
export const getPosts = () => async dispatch => {
try {
const res = await axios.get('/api/posts');
dispatch({
type: GET_POSTS,
payload: res.data
});
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Add like
export const addLike = postId => async dispatch => {
try {
const res = await axios.put(`/api/posts/like/${postId}`);
dispatch({
type: UPDATE_LIKES,
payload: { postId, likes: res.data }
});
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Remove like
export const removeLike = postId => async dispatch => {
try {
const res = await axios.put(`/api/posts/unlike/${postId}`);
dispatch({
type: UPDATE_LIKES,
payload: { postId, likes: res.data }
});
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Add Post
export const addPost = formData => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
try {
const res = await axios.post('/api/posts', formData, config);
dispatch({
type: ADD_POST,
payload: res.data
});
dispatch(setAlert('Post Created', 'success'));
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Delete Post
export const deletePost = id => async dispatch => {
try {
const res = await axios.delete(`/api/posts/${id}`);
dispatch({
type: DELETE_POST,
payload: id
});
dispatch(setAlert('Post Removed', 'success'));
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
//Get post
export const getPost = id => async dispatch => {
try {
const res = await axios.get(`/api/posts/${id}`);
dispatch({
type: GET_POST,
payload: res.data
});
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Add Comment
export const addComment = (postId, formData) => async dispatch => {
const config = {
headers: {
'Content-Type': 'application/json'
}
};
try {
const res = await axios.post(
`/api/posts/comment/${postId}`,
formData,
config
);
dispatch({
type: ADD_COMMENT,
payload: res.data
});
dispatch(setAlert('Comment Added', 'success'));
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
// Delete Comment
export const deleteComment = (postId, commentId) => async dispatch => {
try {
const res = await axios.delete(`/api/posts/comment/${postId}/${commentId}`);
dispatch({
type: REMOVE_COMMENT,
payload: commentId
});
dispatch(setAlert('Comment Removed', 'success'));
} catch (error) {
dispatch({
type: POST_ERROR,
payload: { msg: error.response.data.msg, status: error.response.status }
});
}
};
You're updating the posts array here which is what you use to render the PostItems in Feed.
case UPDATE_LIKES:
return {
...state,
posts: state.posts.map(post =>
post._id === payload.postId ? { ...post, likes: payload.likes } : post
),
loading: false
};
However in Post.js you use the Post object, not the Posts array. Post has not been updated by the UPDATE_LIKES action so your component doesn't re-render.
I am working with React and Redux, and I'm using Redux-form for my forms.
I am trying to load some initial data from database. In the docs it is recommended to use
{
load: loadAccount
}
in connect component, but I'm struggling to set it properly.
I can load initial data in a different way: adding a dispatch action to connect component and call it from componentDidMount, but I would like to understand how to set { load: loadAccount }.
This is my actions file —I show only the action that matters—:
const actions = {
requestProject: () => {
return {
type: C.LOAD_PROJECT_STARTED,
};
},
receiveProject: (id, data) => {
return {
type: C.LOAD_PROJECT_SUCCESS,
id,
data,
};
},
loadProject: (id = '') => {
const url = '/api/projects/';
const encodedURI = isBrowser
? encodeURI(window.location.origin + url + id)
: encodeURI('http://localhost:' + config.SERVER + url + id);
return isBrowser
? function(dispatch) {
dispatch(actions.requestProject());
return fetch(encodedURI)
.then(
(response) => {
return response.json();
},
(error) => {
return console.log('An error occurred.', error);
}
)
.then((data) => {
return dispatch(actions.receiveProject(id, data));
});
}
: fetch(encodedURI)
.then((response) => {
return response.json();
})
.then((data) => {
return data;
})
.catch((error) => {
return Promise.reject(Error(error.message));
});
},
};
export default actions;
Then my reducers —again, only one—:
export const Project = (state = {}, action) => {
switch (action.type) {
case C.LOAD_PROJECT_STARTED:
console.log('LOAD_PROJECT_STARTED');
return Object.assign({}, state, {
isFetching: true,
});
case C.LOAD_PROJECT_SUCCESS:
return Object.assign({}, state, {
...action.data.Project,
isFetching: false,
});
case C.SUBMIT_PROJECT_FORM_STARTED:
return Object.assign({}, state, {
isFetching: true,
});
case C.SUBMIT_PROJECT_FORM_SUCCESS:
return Object.assign({}, state, {
...action.data.Project,
isFetching: false,
});
default:
return state;
}
};
export const form = formReducer;
This is the connect ProjectForm component for the form:
import { connect } from 'react-redux';
import ProjectFormUi from './ProjectFormUi';
import actions from '../../redux/actions';
import { load as loadAccount } from './account'
export const ProjectForm = connect(
(state) => {
return {
initialValues: state.Project,
};
},
(dispatch) => {
return {
loadProject(id) {
dispatch(actions.loadProject(id));
},
onSubmit(data) {
dispatch(actions.sendingProject(data));
},
};
},
{
load: loadAccount,
}
)(ProjectFormUi);
export default ProjectForm;
And finally, the actual form component, ProjectFormUi.
import React from 'react';
import { Field, reduxForm } from 'redux-form';
class ProjectFormUi extends React.Component {
constructor(props) {
super(props);
this.state = {
};
}
componentDidMount() {
this.props.loadProject(1);
}
static getDerivedStateFromProps(newProps, prevState) {
return newProps != prevState ? newProps : null;
}
componentDidUpdate(prevProps) {
if (this.props.Project !== prevProps.Project) {
this.setState({
isFetching: this.props.Project.isFetching,
});
}
}
render() {
const { handleSubmit, load, pristine, reset, submitting } = this.props;
return (
<form onSubmit={handleSubmit}>
<div>
<label>First Name</label>
<div>
<Field name="title" component="input" type="text" placeholder="First Name" />
</div>
</div>
<div>
<button type="submit" disabled={submitting}>
Submit
</button>
<button type="button" disabled={pristine || submitting} onClick={reset}>
Undo Changes
</button>
</div>
</form>
);
}
}
export default reduxForm({
form: 'ProjectForm',
enableReinitialize: true,
})(ProjectFormUi);
As I said, currently I'm loading data in connect ProjectForm component with:
loadProject(id) {
dispatch(actions.loadProject(id));
},
which is called from componentDidMount in ProjectFormUi component.
But I would like to understand how to load data as in the docs, setting
{
load: loadAccount
}
in connect ProjectForm component.
I want to make a transition from a link to an article, to the article itself, when I click on a link, the construction is triggered: Not found. Tell me what I'm doing wrong.
task_conteiner.js
import React, { Component } from 'react';
import { browserHistory } from 'react-router';
import { Link } from 'react-router';
export default class TasksContainer extends Component {
onShowMoreTask(id) {
browserHistory.push(`#/tasks/${id}`);
location.reload()
}
renderTasks() {
let filterComleted = this.props.tasks.tasks
let str = '★'
let style
if (this.props.Completed === "task.completed") {
filterComleted = filterComleted.filter(task => task.completed);
} else {
filterComleted = filterComleted.filter(task => !task.completed);
}
return filterComleted.map((task) => {
if(task.completed){
style = {
textDecoration: 'line-through'
}
}
return (
<div key={task.id}>
<li className="todo">
<div className="container">
<div className="col-md-3" onClick={() => this.onShowMoreTask( task.id )}>{ task.title }</div>
<div className="col-md-3" style={style}>{ task.description }</div>
<div className="col-md-3" style={style}>{ task.due_date }</div>
<div className="col-md-1 star">{ str.repeat(task.priority) }</div>
<span onClick={() => this.props.onCompletedTask(task.id, task.completed)} className={task.completed ? "glyphicon glyphicon-repeat" : "glyphicon glyphicon-ok"} title={task.active ? "Mark active" : "Mark completed"}></span>
<span onClick={() => this.props.onEditTask( this.props.editId, task.id )} className="glyphicon glyphicon-pencil" title="Edit task"></span>
<span onClick={() => this.props.onDeleteTask(task.id)} className="glyphicon glyphicon-trash" title="Delete"></span>
</div>
</li>
</div>
);
});
}
render() {
return (
<div>
{this.renderTasks()}
</div>
);
}
}
task_details.js
import React, { Component, PropTypes } from 'react';
import { connect } from 'react-redux';
import Exit from '../authentication/exit';
import { browserHistory } from 'react-router';
import { getTask } from '../../actions/tasks';
import TasksList from './tasks_list';
import '../../index.css';
import Link from 'react-router'
class TaskDetails extends Component {
componentDidMount () {
let id = this.props.params.id;
this.props.onGetTask(id);
};
render() {
const { task } = this.props
console.log(this.props.location.pathname, "xxxxxxxx")
return (
<div>
{ this.props.task ?
<div className="container">
<h2 className="text-center">{task.title}</h2>
<div className="col-md-2">
<h4 className="pull-right"><i>{task.due_date}</i></h4>
</div>
<div className="clearfix"></div>
<div className="description">
<p>{task.description}</p>
</div>
</div>
:
<div className="container">
<div><h2>Not found</h2></div>
</div>
}
</div>
);
}
};
export default connect(
state => ({
task: state.tasks.item
}),
dispatch => ({
onGetTask: (id) => {
dispatch(getTask(id));
},
})
)(TaskDetails);
The task_details triggers the construction:
<div className="container">
<div><h2>Not found</h2></div>
</div>
There is an error in the console:
/tasks/6 xxxxxxxx
task_details.js:50 Uncaught TypeError: (0 , _tasks.getTask) is not a function
at Object.onGetTask (task_details.js:50)
at TaskDetails.componentDidMount (task_details.js:14)
at ReactCompositeComponent.js:265
at measureLifeCyclePerf (ReactCompositeComponent.js:75)
at ReactCompositeComponent.js:264
at CallbackQueue.notifyAll (CallbackQueue.js:76)
at ReactReconcileTransaction.close (ReactReconcileTransaction.js:80)
at ReactReconcileTransaction.closeAll (Transaction.js:206)
at ReactReconcileTransaction.perform (Transaction.js:153)
at batchedMountComponentIntoNode (ReactMount.js:126)
Thank you for your help.
and
reducers/task.js
export default function tasks(state = {
tasks: [],
edit: '',
sortBy: {title: "priority", asc: "desc"}
}, action) {
switch (action.type) {
case "FETCH_TODOS_SUCCESS":
return {
...state,
tasks: action.payload
};
case "GET_TASKS":
return {
...state,
tasks: action.payload
}
case "ADD_TASK":
return {
...state,
tasks: [action.payload, ...state.tasks]
}
case "DELETE_TASK":
state.tasks = state.tasks.filter(t => t.id !== action.payload);
return {
...state,
tasks: [...state.tasks]
}
case "EDIT_TASK":
state.tasks = state.tasks.filter(t => t.id !== action.payload.id);
return {
...state,
tasks: [action.payload, ...state.tasks]
}
case "COMPLITED_TASK":
state.tasks = state.tasks.filter(t => t.id !== action.payload.id);
return {
...state,
tasks: [action.payload, ...state.tasks]
};
case "EDIT_ID":
return {
...state,
edit: action.payload
}
case "SORT_BY":
return {
...state,
sortBy: action.payload
}
default:
return state;
}
}
action/task.js
import axios from 'axios';
import cookie from 'react-cookies';
//const API_URL = `https://evening-taiga-79121.herokuapp.com/todos`;
const API_URL = `http://localhost:3000/todos`;
let headers = { 'Content-Type': 'application/json', };
const token = cookie.load('token');
export function fetchTasks(user_id){
return function(dispatch, getState) {
let body = JSON.stringify({ token: token });
headers['Authorization'] = `Bearer ${token}`;
axios.get(`${API_URL}`, { headers, body })
.then(res => {
if (res.status === 200) {
dispatch({ type: 'GET_TASKS', payload: res.data });
}
})
.catch(e => {
console.error("error: ", e);
})
}
}
export function deleteTask(id){
return function(dispatch, getState) {
let body = { token: token };
axios.delete(`${API_URL}/${id}`, { params: body, headers: headers })
.then(res => {
dispatch({ type: 'DELETE_TASK', payload: id });
})
.catch(id => {
console.error("error", id);
})
}
}
export function addTask(task){
return function(dispatch, getState) {
let body = JSON.stringify({todo: task, token: token});
console.log(body);
axios.post(API_URL, body, { headers: headers })
.then(res => {
dispatch({ type: 'ADD_TASK', payload: res.data });
})
.catch(e => {
console.error(e);
})
}
}
export function completedTask(id, complete){
return function(dispatch, getState) {
if (complete === true) {
complete = false
} else {
complete = true
}
let task = {id: id, completed: complete};
let body = {todo: task, token: token};
axios.patch(`${API_URL}/${task.id}`, body, { headers: headers })
.then(res => {
dispatch({ type: 'COMPLITED_TASK', payload: res.data });
})
.catch(e => {
console.error("error: ", e);
})
}
}
export function sortTasks(sortBy){
return function(dispatch, getState) {
let body = JSON.stringify({ token: token, sortByTitle: sortBy.title, sortByAsc: sortBy.asc });
axios.post(`${API_URL}/sort`, body, { headers: headers })
.then(res => {
console.log(res);
if (res.status === 200) {
dispatch({ type: 'SORT_BY', payload: sortBy });
dispatch({ type: 'FETCH_TODOS_SUCCESS', payload: res.data });
}
})
.catch(e => {
console.error("error: ", e);
})
}
}
export function editTask(task){
return function(dispatch, getState) {
let body = JSON.stringify({todo: task, token: token});
axios.patch(`${API_URL}/${task.id}`, body, { headers: headers })
.then(res => {
dispatch({ type: 'EDIT_TASK', payload: res.data });
})
.catch(e => {
console.error("error: ", e);
})
}
}
When I fill one field, the letters are transferred to another, can be transferred at all in a text block of another component. Before that everything worked fine, just a moment something went wrong, do not understand that. Please help.gif with problem here
my component:
import React from 'react'
import axios from 'axios'
import {Row, Col, FormGroup, ControlLabel, FormControl, HelpBlock} from 'react-bootstrap'
import SelectizeSimple from '../../SelectizeSimple'
import {connect} from 'react-redux'
import {bindActionCreators} from 'redux'
import {
setUserSecureId,
setUserName,
setCompany,
setAddressFirstLine,
setAddressSecondLine,
setCity,
setState,
setZip,
setVat,
setEmail
} from 'store/user'
class PersonalInfo extends React.Component{
constructor(props){
super(props)
this.state = {
inputName:''
}
}
submitFormPersonalInfo = (event) => {
axios.post('', {
query: 'mutation UpdateUser($form:UpdateUserInput!) { update_user (input:$form) { id } }',
variables: {form: this.props.updateUser},
}).then(res => {
console.log(res)
})
}
changeUserName = (event) => {
this.props.actions.setUserName(event.target.value)
}
changeCompany = (event) => {
this.props.actions.setCompany(event.target.value)
}
changeAddressFirstLine = (event) => {
this.props.actions.setAddressFirstLine(event.target.value)
}
changeAddressSecondLine = (event) => {
this.props.actions.setAddressSecondLine(event.target.value)
}
changeCity = (event) => {
this.props.actions.setCity(event.target.value)
}
changeState = (event) => {
this.props.actions.setState(event.target.value)
}
changeZIP = (event) => {
this.props.actions.setZip(event.target.value)
}
changeVat = (event) => {
this.props.actions.setVat(event.target.value)
}
changeEmail = (event) => {
this.props.actions.setEmail(event.target.value)
}
render(){
return(
<div>
<div className="tab-wrap-inner-header">
<div className="title_primary title_primary__inner">Personal Info</div>
</div>
<div className="form-wrap form-wrap__large-right-gap">
<form action="" className="form-default">
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
E-mail
</ControlLabel>
<div className="form-edit-box">
<FormControl placeholder="email#gmail.com"
onChange={this.changeEmail}
value={this.props.updateUser.email}/>
<button className="btn-edit btn-edit__left-gap">
Edit
</button>
</div>
<HelpBlock>This address will appear on your License.</HelpBlock>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Password
</ControlLabel>
<div className="form-edit-box">
<FormControl type="password" placeholder="*********"></FormControl>
<button className="btn-edit btn-edit__left-gap">
Edit
</button>
</div>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Name
</ControlLabel>
<FormControl
onChange={this.changeUserName}
value={this.props.updateUser.name}/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Company Name (optional)
</ControlLabel>
<FormControl
onChange={this.changeCompany}
value={this.props.updateUser.company}
/>
<HelpBlock className="flexbox align-center">
<div className="radio-box radio-box__square radio-box__nogap">
<FormControl type="checkbox" id="radioGroup11"></FormControl>
<ControlLabel htmlFor="radioGroup11" className="radio-box__pseudo-radio"></ControlLabel>
</div>
<label htmlFor="radioGroup11" className="label-default">Hide information about my country from my profile</label>
</HelpBlock>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Adress Line 1
</ControlLabel>
<FormControl
onChange={this.changeAddressFirstLine}
value={this.props.updateUser.address_first_line}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
Adress Line 2 (optional)
</ControlLabel>
<FormControl
onChange={this.changeAddressSecondLine}
value={this.props.updateUser.address_second_line}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
City
</ControlLabel>
<FormControl
onChange={this.changeCity}
value={this.props.updateUser.city}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
State / Province / Region
</ControlLabel>
<FormControl
onChange={this.changeState}
value={this.props.updateUser.state}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
ZIP / Postal Code
</ControlLabel>
<FormControl
onChange={this.changeZIP}
value={this.props.updateUser.zip}
/>
</FormGroup>
<FormGroup className="form-group__indent-bot-big">
<ControlLabel>
VAT number (if applicable)
</ControlLabel>
<FormControl
onChange={this.changeVat}
value={this.props.updateUser.vat}
/>
</FormGroup>
<button type="button" className="btn btn-primary btn-primary__submit" onClick={this.submitFormPersonalInfo}>
Save Changes
</button>
</form>
</div>
</div>
)
}
}
const mapStateToProps = (state) => {
return ({
updateUser: state.user.updateUser,
})
}
const mapDispatchToProps = (dispatch) => ({
actions: bindActionCreators({
setUserSecureId,
setUserName,
setCompany,
setAddressFirstLine,
setAddressSecondLine,
setCity,
setState,
setZip,
setVat,
setEmail
}, dispatch),
})
export default connect(mapStateToProps, mapDispatchToProps)(PersonalInfo)
my reducer:
import axios from 'axios'
export const GET_USER_INFO = 'GET_USER_INFO'
export const GET_CURRENT_USER = 'GET_CURRENT_USER'
export const SET_USER_AVATAR = 'SET_USER_AVATAR'
export const SET_USER_BANNER = 'SET_USER_BANNER'
export const SET_USER_SECURE_ID = 'SET_USER_SECURE_ID'
export const SET_USER_NAME = 'SET_USER_NAME'
export const SET_COMPANY = 'SET_COMPANY'
export const SET_ADDRESS_FIRST_LINE = 'SET_COMPANY'
export const SET_ADDRESS_SECOND_LINE = 'SET_COMPANY'
export const SET_CITY = 'SET_CITY'
export const SET_STATE = 'SET_STATE'
export const SET_ZIP = 'SET_ZIP'
export const SET_VAT = 'SET_VAT'
export const SET_BIOGRAPHY = 'SET_VAT'
export const ADD_TRACK_TO_FAVORITES = 'ADD_TRACK_TO_FAVORITES'
export const SET_SOCIAL_LINKS = 'SET_SOCIAL_LINKS'
export const SET_EMAIL = 'SET_EMAIL'
const initialState = {
userInfo: {},
currentUser: {},
updateUser: {
id: '',
name: "",
avatar: "",
address_first_line: "",
address_second_line: "",
city: "",
state: "",
zip: "",
vat: "",
company: "",
biography: "",
secure_id: "",
},
favorite_tracks:[],
social_links:[]
}
export const getUserInfo = (user) =>
(dispatch, getState) => {
var state = getState();
return axios.post(API_URL, {
query: "query User($id:ID) {user(id:$id) { id name email avatar social_links { type url } banner email biography address_first_line address_second_line city state zip vat company secure_id created_at is_current tracks { id name logo created_at composer publisher } favorite_tracks { id name logo created_at is_favorite user { id name } } followers { id name tracks_count } followings { id name tracks_count }}}",
variables: {id: user}
}).then(res => {
dispatch({
type: GET_USER_INFO,
data: res.data.data.user
})
})
}
export const getCurrentUser = () =>
(dispatch, getState) => {
var state = getState();
return axios.post(API_URL, {
query: "{ current_user () { id name email biography created_at is_current } }"
}).then(res => {
dispatch({
type: GET_CURRENT_USER,
data: res.data.data.current_user
})
})
}
export const AddTrackToFavorites = (id) =>
(dispatch, getState) => {
var state = getState();
return axios.post(API_URL, {
query: "mutation AddTrackToFavorites($input:AddTrackToFavoritesInput!) { add_track_to_favorites (input:$input) { id } }",
variables: { input: { id: id } }
}).then(res => {
})
}
export const RemoveTrackFromFavorites = (id) =>
(dispatch, getState) => {
var state = getState();
return axios.post(API_URL, {
query: "mutation RemoveTrackFromFavorites($input:RemoveTrackFromFavoritesInput!) {remove_track_from_favorites (input:$input) { id }}",
variables: { input: { id: id } }
}).then(res => {
})
}
export const setUserAvatar = (avatar) =>
(dispatch) => {
dispatch({
type: SET_USER_AVATAR,
data: avatar
})
}
export const setUserBanner = (banner) =>
(dispatch) => {
dispatch({
type: SET_USER_BANNER,
data: banner
})
}
export const setUserName = (name) =>
(dispatch) => {
dispatch({
type: SET_USER_NAME,
data: name
})
}
export const setCompany = (company) =>
(dispatch) => {
dispatch({
type: SET_COMPANY,
data: company
})
}
export const setAddressFirstLine = (address) =>
(dispatch) => {
dispatch({
type: SET_ADDRESS_FIRST_LINE,
data: address
})
}
export const setAddressSecondLine = (address) =>
(dispatch) => {
dispatch({
type: SET_ADDRESS_SECOND_LINE,
data: address
})
}
export const setCity = (city) =>
(dispatch) => {
dispatch({
type: SET_CITY,
data: city
})
}
export const setState = (state) =>
(dispatch) => {
dispatch({
type: SET_STATE,
data: state
})
}
export const setEmail = (state) =>
(dispatch) => {
dispatch({
type: SET_EMAIL,
data: state
})
}
export const setZip = (zip) =>
(dispatch) => {
dispatch({
type: SET_ZIP,
data: zip
})
}
export const setVat = (vat) =>
(dispatch) => {
dispatch({
type: SET_VAT,
data: vat
})
}
export const setBiography = (biography) =>
(dispatch) => {
dispatch({
type: SET_BIOGRAPHY,
data: biography
})
}
export const setSocialLinks = (links) =>
(dispatch) => {
dispatch({
type: SET_SOCIAL_LINKS,
data: links
})
}
export const setUserSecureId = (value) =>
(dispatch) => {
dispatch({
type: SET_USER_SECURE_ID,
data: value,
})
}
const ACTION_HANDLERS = {
[GET_USER_INFO]: (state, action) => {
return ({
...state,
userInfo: {
...state.userInfo,
created_at: action.data.created_at,
is_current: action.data.is_current,
tracks: action.data.tracks,
followers: action.data.followers,
followings: action.data.followings,
favorite_tracks: action.data.favorite_tracks,
},
updateUser: {
...state.updateUser,
id: action.data.id,
email: action.data.email,
secure_id: action.data.secure_id,
name: action.data.name,
avatar: action.data.avatar,
banner: action.data.banner,
company: action.data.company,
address_first_line: action.data.address_first_line,
address_second_line: action.data.address_second_line,
city: action.data.city,
state: action.data.state,
zip: action.data.zip,
vat: action.data.vat,
biography: action.data.biography,
social_links: action.data.social_links
},
favorite_tracks: action.data.favorite_tracks,
social_links: action.data.social_links
})
},
[GET_CURRENT_USER]: (state, action) => {
return ({
...state,
currentUser: action.data
})
},
[SET_USER_AVATAR]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
avatar: action.data
}
})
},
[SET_USER_BANNER]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
banner: action.data
}
})
},
[SET_USER_NAME]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
name: action.data
}
})
},
[SET_COMPANY]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
company: action.data
}
})
},
[SET_ADDRESS_FIRST_LINE]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
address_first_line: action.data
}
})
},
[SET_ADDRESS_SECOND_LINE]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
address_second_line: action.data
}
})
},
[SET_CITY]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
city: action.data
}
})
},
[SET_STATE]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
state: action.data
}
})
},
[SET_ZIP]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
zip: action.data
}
})
},
[SET_VAT]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
vat: action.data
}
})
},
[SET_BIOGRAPHY]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
biography: action.data
}
})
},
[SET_USER_SECURE_ID]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
secure_id: action.data
}
})
},
[SET_EMAIL]: (state, action) => {
return ({
...state,
updateUser: {
...state.updateUser,
email: action.data
}
})
},
[ADD_TRACK_TO_FAVORITES]: (state, action) => {
return ({
...state,
favorite_tracks: action.data
})
},
[SET_SOCIAL_LINKS]: (state, action) => {
return ({
...state,
social_links: action.data
})
},
}
export default function userReducer(state = initialState, action) {
const handler = ACTION_HANDLERS[action.type]
return handler ? handler(state, action) : state
}
Your problem is because same constants for multiple redux action ids:
export const SET_COMPANY = 'SET_COMPANY'
export const SET_ADDRESS_FIRST_LINE = 'SET_COMPANY'
export const SET_ADDRESS_SECOND_LINE = 'SET_COMPANY'
need replace with:
export const SET_COMPANY = 'SET_COMPANY'
export const SET_ADDRESS_FIRST_LINE = 'SET_ADDRESS_FIRST_LINE'
export const SET_ADDRESS_SECOND_LINE = 'SET_ADDRESS_SECOND_LINE'