I'm making Log In Stuff for my application.All I want to is to update "username" in TodoApp.jsx component and set it to the same value as Login.jsx "Username"
Any suggestions?
TodoApp.jsx
class ToDoApp extends Component {
state = {
username:'',
inputValue: '',
todos: [],
currentPage: 1,
pageCount: 1,
itemsPerPage: 10,
};
This function creates new item and adds it to the todos:[] in state
addItem = () => {
let {todos} = this.state
if (this.inpRef.current.value === '') {
return alert('We dont do that here....')
} else {
axios
.post(`http://localhost:8080/add`, {
todo: this.inpRef.current.value,
checked: false,
})
.then((res) => {
this.setState({
todos:[...todos,{todo:res.data.todo,_id:res.data._id,checked:false}]
})
})
.catch((err) => {
console.log("err", err);
});
this.setPageCount()
}
this.inpRef.current.value = ''
console.log('--------this.state.todos', this.state.todos);
}
Here is Login.jsx code:
class Login extends Component {
state = {
username:'',
password:'',
}
This is a function which handles Log In.
handleLogIn = () => {
const { username, password } = this.state
if (password.length === 0 || username.length === 0 ) {
alert('Incorrect Login!')
} else {
axios
.post(`http://localhost:8080/logIn`, {
username: username,
password: password,
})
.then((res) => {
this.props.history.push("/todoapp");
console.log('res',res)
})
.catch((err) => {
console.log("err", err);
});
}
}
You can use the props property of React.js to achieve that.
If we assume that you've called your Login component in ToDoApp
(which makes ToDoApp the father and Login the child component); then what you can do is to define a method that sets the username state of the child component.
For example:
Login Component: Call the handle_username function of the father component once you set the username state.
this.setState({username: (username input)})
this.props.handle_username(this.state.username)
ToDoApp Component: Define the function handle_username and pass it as props to the Login Component.
handle_username = (username) => {
this.setState({username: username})
}
End pass it as props:
<Login handle_username={this.handle_username}/>
You can using Redux to achieve this.
passing props between this two components.
Calling API to update data and fetch API on another page.
Using browser localstorage to store the input data.
Related
What I want to do
When a child component first rendering, I would like to use the value in props from a parent component
Problem
When a child component is first rendered, props is not set to state in the child component
I am a beginner to React. I am trying to use props in order to call API by axios in componentDidMount in a child component. I mean, what I am doing is calling API in a parent component and setting data from this API to a child component as props.
However, when I try to do that, props is not set to state in a child component.
For example, when I retrieve product which has some category, I type localhost:3000/api/parent/?category=1/. But, my console.log shows me localhost:3000/api/parent/?category=undefined because I guess props is not set when a child component first rendering.
Actually, I can see category object in state like below.
I guess props is completely set to state after the child component finish first rendering.
How could I set props which comes from API to state?
Although I tried many solutions I found on the stackoverflow, I got stuck at this problem so long time.
I would like you to tell me some solutions.
Thank you very much.
== == ==
My code is like this.
Parent Component
class Top extends Component {
constructor(props) {
super(props);
this.state = {
loginUser: '',
categories: [],
};
}
async componentDidMount() {
const localhostUrl = 'http://localhost:8000/api/';
const topCategoryList = ['Smartphone', 'Tablet', 'Laptop'];
let passCategoryToState=[]
axios
.get(localhostUrl + 'user/' + localStorage.getItem('uid'))
.then((res) => {
this.setState({ loginUser: res.data });
})
.catch((err) => console.log(err));
await Promise.all(
topCategoryList.map(async (category) => {
await axios.get(localhostUrl + 'category/?name=' + category).then((res) => {
passCategoryToState=[...passCategoryToState, res.data]
console.log(passCategoryToState);
});
})
);
this.setState({categories : passCategoryToState})
}
render() {
if (!this.props.isAuthenticated) {
return <p> Developing now </p>;
}
if (this.state.loginUser === '' ) {
return <CircularProgress />;
} else {
return (
<>
<Header loginUser={this.state.loginUser} />
<Give_Item_List
axiosUrl="http://localhost:8000/api/"
subtitle="Smartphone Items"
loginUser={this.state.loginUser}
category={this.state.categories[0]}
/>
</>
);
}
}
}
export default Top;
And, child component
class Give_Item_List extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
loginUser: this.props.loginUser,
category: this.props.category,
};
}
async componentDidMount() {
let pickedGiveItems;
await this.setState({ loading: true });
await axios
.get(this.props.axiosUrl + 'giveitem/?category=' + this.state.category.id)
.then((res) => {
pickedGiveItems = res.data;
console.log(pickedGiveItems);
})
.catch((err) => console.log('Not found related to Items'));
this.setState({ loading: false });
}
render() {
if (this.state.loading == true) {
return <CircularProgress />;
}
return <h1>Give_Item_List</h1>;
}
}
export default Give_Item_List;
==============
Edit:Change to componentDidUpdate
componentDidUpdate(prevProps) {
if(prevProps.category != this.props.category){
let pickedGiveItems;
this.setState({ loading: true });
axios
.get(this.props.axiosUrl + 'giveitem/?category=' + this.props.category.id)
.then((res) => {
pickedGiveItems = res.data;
console.log(pickedGiveItems);
})
.catch((err) => console.log('NotFount'));
this.setState({ loading: false });
}
}
It still doesn't work...
In the constructor, this.props is undefined, but you can access the props directly.
constructor(props) {
super(props);
this.state = {
loading: false,
loginUser: props.loginUser,
category: props.category,
};
}
However, I should note now that storing props in state is a common react anti-pattern, you should always consume prop values from this.props where you need them.
For example:
async componentDidMount() {
let pickedGiveItems;
await this.setState({ loading: true });
await axios
.get(this.props.axiosUrl + 'giveitem/?category=' + this.props.category.id)
.then((res) => {
pickedGiveItems = res.data;
console.log(pickedGiveItems);
})
.catch((err) => console.log('Not found related to Items'));
this.setState({ loading: false });
}
You also can't await a react state update since it isn't an async function nor does it return a Promise. Just provide an initial true loading state and toggle false when the fetch request resolves.
class Give_Item_List extends Component {
constructor(props) {
super(props);
this.state = {
loading: true
};
}
async componentDidMount() {
let pickedGiveItems;
await axios
.get(this.props.axiosUrl + 'giveitem/?category=' + this.props.category.id)
.then((res) => {
pickedGiveItems = res.data;
console.log(pickedGiveItems);
})
.catch((err) => console.log('Not found related to Items'));
this.setState({ loading: false });
}
render() {
if (this.state.loading) {
return <CircularProgress />;
}
return <h1>Give_Item_List</h1>;
}
}
I know that one should generally have functional stateless components in Redux. However, I have quite a nice react statefull component and try to redesign it to work with Redux. Why? I do not want to use store to handle every bit of this componet logic while this component does its job fine; besides I'd like to keep lifycle methods. The problem I have is that do not see way to dispatch state of this component.
class LoginX extends React.Component {
constructor(props) {
super(props);
this.state = { login: '', password: '' };
}
handleChange = (e) => {
this.setState({
[e.currentTarget.name]: e.currentTarget.value,
});
}
render(){...}
}
That is simplified class, I removed 90% to keep it readable.
const mapStateToProps = (state) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
onClick: (e) => {e.preventDefault(); console.log('dispatch'); dispatch({
type: 'LOGIN_SUBMIT',
payload: {
token: {...LoginX.state}
}
})}
}};
let Login = connect(mapStateToProps, mapDispatchToProps)(LoginX);
export default Login;
That is rest of code. You see, I have written token: {...LoginX.state} to indicate what I wanna have rather. Yes, I know that Redux components should be functional, but maybe there is workaround for case like mine?
It's not wrong per se to define React components as classes. If the React version you are using does not support hooks and you are using features that only classes provide (state and component lifecycles) you are fine defining your components as classes.
If you want to dispatch an action with some data that is present in your component, you should provide the data from the component itself when calling the action creator.
In your case, I would do something like:
class LoginX extends React.Component {
constructor(props) {
super(props);
this.state = { login: '', password: '' };
}
handleChange = (e) => {
this.setState({
[e.currentTarget.name]: e.currentTarget.value,
});
}
onClick = (e) => {
const { login, password } = this.state;
e.preventDefault();
this.props.onSubmit({ login, password });
}
render() {
...
<button onClick={this.onClick}>submit</button>
}
}
const mapStateToProps = (state) => {
return state;
}
const mapDispatchToProps = (dispatch) => {
return {
onSubmit: (credentials) => {
console.log('dispatch');
dispatch({
type: 'LOGIN_SUBMIT',
payload: {
token: credentials
}
})
}
}
};
let Login = connect(mapStateToProps, mapDispatchToProps)(LoginX);
export default Login;
Aim :
I want to put firstName and lastName on my Navbar. So, I'm using axios request by id with userId
EDIT: Thanks to #Isaac, I have no more infinite loop when I'm using componentWillUpdate() now.
Problem : Data doesn't change (firstName and lastName) when I'm logout and login with another account
No problems from servers.
here a picture :
Description : I've login as a & g (firstName and lastName), then I've logout and login as j & j.
navbar.js:
import React, { Component } from 'react';
import { fade } from '#material-ui/core/styles/colorManipulator';
import { withStyles } from '#material-ui/core/styles';
import { connect } from 'react-redux';
import AuthA from '../store/actions/AuthA';
import { withRouter } from 'react-router-dom';
import '../Navbar.css';
import NavbarV from './NavbarV';
import PropTypes from 'prop-types';
import axios from 'axios';
class NavbarC extends Component {
constructor(props){
super(props);
this.state = {
client:[]
}
}
componentWillMount(){
this.getUser();
}
getUser(){
axios.get (`http://localhost:3002/api/clients/${localStorage.getItem("userId")}?access_token=${localStorage.getItem("token")}`)
.then(res => {
this.setState({client: res.data}, () => {
console.log(this.state)
})
})
}
shouldComponentUpdate(nextState){
return (this.state.client.firstName !== nextState.firstName ||
this.state.client.lastName !== nextState.lastName);
}
componentWillUpdate(){
this.getUser();
console.log(this.state)
}
logout = () => {
this.props.authfn.logout();
};
render() {
return(
<NavbarV logout = {this.logout}
firstName={this.state.client.firstName}
lastName={this.state.client.lastName}
userId={this.props.userId}
auth = {this.props.auth}
classes={this.props.classes}/>
)
}
}
NavbarC.propTypes = {
auth: PropTypes.bool.isRequired,
firstName: PropTypes.string.isRequired,
lastName: PropTypes.string.isRequired
};
const mapStateToProps = (state) => {
return {
auth: state.AuthR.auth,
firstName: state.AuthR.firstName,
lastName: state.AuthR.lastName,
userId: state.AuthR.userId
};
};
const mapDispatchToProps = dispatch => {
return {
authfn: AuthA(dispatch)
}
};
export default connect(mapStateToProps, mapDispatchToProps) (withStyles(styles)(withRouter(NavbarC)));
If someone have a solution or any questions, I'm here :)
thank you all in advance
First of all, you should avoid componentWillUpdate lifecycle as it's been deprecated.
And for your case, this.getUser(); will be triggered to pull data which then trigger this.setState({client: res.data}). When the app executing this.setState(), your component will be re-render so there's no need to have any other componentLifeCycle.
class NavbarC extends Component {
state = { client:[], userID: null, token: null };
componentDidMount(){
this.setState({
userID: localStorage.getItem("userId"),
token: localStorage.getItem("token")
}, () => {
this.getUser();
})
}
getUser(){
axios.get (`http://localhost:3002/api/clients/${this.state.userID}?access_token=${this.state.token}`)
.then(res => {
this.setState({ client: res.data }, () => {
console.log(this.state)
})
})
}
componentDidUpdate(prevProps, prevState){
if(prevState.userID !== this.state.userID) {
this.getUser();
}
}
logout = () => this.props.authfn.logout();
render() {
return(
<NavbarV
logout = {this.logout}
firstName={this.state.client.firstName}
lastName={this.state.client.lastName}
userId={this.props.userId}
auth = {this.props.auth}
classes={this.props.classes} />
)}
}
I solve it !
This is a solution :
componentDidMount(){
this.setState({
userId: localStorage.getItem("userId"),
token: localStorage.getItem("token")
}, () => {
this.getUser();
})
}
getUser = () => {
axios.get (`http://localhost:3002/api/clients/${this.state.userId}?access_token=${this.state.token}`)
.then(res => {
this.setState({ client: res.data, userId: localStorage.getItem("userId") }, () => {
console.log(this.state)
})
})
}
componentDidUpdate(prevProps, prevState){
if(prevState.userId !== this.props.userId) {
this.setState({ userId: this.props.userId }, () => {
this.getUser();
})
}
}
I want execute a custom function when the field become valid?
Something like this..
<Field name="postal-code" onValid={...} />
The reason is that I want make fetch (GET) to get address from API once the user type an valid Postal code
You can define the custom function inside the component class or outside the component.
// outside the component (best suited for functional component)
const onValidFn = () => {
// perform action
}
// inside the component (best suited for stateful component)
onValidFn() {
// perform action
}
If you want to access this inside the onValidFn method, you may bind this inside the constructor or use public class method:
onValidFn = () => {
// perform action
console.log(this)
}
// if your method is defined in outer scope
<Field name="postal-code" onValid={onValidFn} />
// if your method is defined in inner scope (inside class)
<Field name="postal-code" onValid={this.onValidFn} />
You can solve it like this:
have Loader component which loads data if it gets an URL
pass URL to this component if touched[fieldName] && !errors[fieldName]
Loader component can be like
import { PureComponent } from 'react';
import PropTypes from 'prop-types';
import superagent from 'superagent'; // swap to your xhr library of choice
class Loader extends PureComponent {
static propTypes = {
url: PropTypes.string,
onLoad: PropTypes.func,
onError: PropTypes.func
}
static defaultProps = {
url: '',
onLoad: _ => {},
onError: err => console.log(err)
}
state = {
loading: false,
data: null
}
componentDidMount() {
this._isMounted = true;
if (this.props.url) {
this.getData()
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.url !== this.props.url) {
this.getData(nextProps)
}
}
componentWillUnmount() {
this._isMounted = false
}
getData = (props = this.props) => {
const { url, onLoad, onError } = props;
if (!url) {
return
}
this.setState({ data: null, loading: true });
const request = this.currentRequest = superagent.
get(url).
then(({ body: data }) => {
if (this._isMounted && request === this.currentRequest) {
this.setState({ data, loading: false }, _ => onLoad({ data }));
}
}).
catch(err => {
if (this._isMounted && request === this.currentRequest) {
this.setState({ loading: false });
}
onError(err);
});
}
render() {
const { children } = this.props;
return children instanceof Function ?
children(this.state) :
children || null;
}
}
If no url is passed, it does nothing. When url changes - it loads data.
Usage in Formik render/children prop:
<Loader
{...(touched[fieldName] && !errors[fieldName] && { url: URL_TO_FETCH })}
onLoad={data => ...save data somewhere, etc.}
/>
I am trying to implement auth (sign up/out) using React + Redux (SSR and Thunks). I have no idea why components are not updatating when Redux state updates...
This is the component that should get rerendered:
class Navbar extends React.Component {
constructor(props) {
super(props);
this.state = {
loggedIn: props.authentication.loggedIn
};
}
render() {
let links = null;
if (this.state.loggedIn) {
links = ...
} else {
links = ...
}
return (<Toolbar>{links}</Toolbar>)
}
}
const mapStateToProps = state => {
return {
authentication: state.authentication
}
}
const mapDispatchToProps = dispatch => {
return {
signOut: () => {dispatch(userActions.logout())}
}
}
const AuthNavbar = connect(mapStateToProps, mapDispatchToProps)(Navbar)
export default AuthNavbar;
And that is my reducer:
const reducers = {
authentication,
registration,
alert
}
const todoApp = combineReducers(reducers)
export default todoApp
Authentication reducer:
const authentication = (state = initialState, action) => {
switch (action.type) {
...
case userConstants.LOGIN_SUCCESS:
return Object.assign({}, state, {
loggedIn: true,
loggingIn: false,
user: action.user
});
...
default:
return state;
}
}
And The Action - Login:
function login(email, password) {
return dispatch => {
dispatch({type: userConstants.LOGIN_REQUEST, email});
userService.login(email, password).then(
user => {
dispatch({type: userConstants.LOGIN_SUCCESS, user});
},
error => {
dispatch({ type: userConstants.LOGIN_FAILURE });
dispatch({type: alertActions.error(error)});
}
);
}
}
UserService.login is a function that calls and api witch fetch.
Looks like Action gets fired as it should, Redux state gets updated, but the component does not update:
Double checked Redux Dev Tools - state does get updated, so there must be a problem with the way I am using connect utilities?
You are storing the logedin props in the state inside the constructor, which will run only once in the life time of the component.
When a new prop is coming back you are not updating the state.
Either use the props directly:
if (this.props.authentication.loggedIn) {
links = ...
Or update the state in componentWillReceiveProps
componentWillReceiveProps(nextProps){
// update the state with the new props
this.setState({
loggedIn: nextProps.authentication.loggedIn
});
}
Your render function is dependent on state.loggedIn, but state.loggedIn is not changing; only this.props.authentication.loggedIn is changing in response to the action. Your component, in its current form, does not need state. You can remove it to make this work:
class Navbar extends React.Component {
render() {
let links = null;
if (this.props.authentication.loggedIn) {
links = ...
} else {
links = ...
}
return (<Toolbar>{links}</Toolbar>)
}
}