Â
How to update state of an object
We have a user object
setUser({ ...user, name: "Peter" });
How to update state of list/array of objects
We have a shopping cart which contains a list of menu items
const [shopCart, setShopCart] = useState([{ name: "", price: 0, uuid: "" }]);
add item to shopping cart
const addMenuItemToCart = (menuItem: any) => { console.log("addMenuItemToCart, ", menuItem) setShopCart(shopCart => { shopCart.push(menuItem); const newState = shopCart.map(obj => { // đď¸ otherwise return object as is return obj; }); return newState; }); }
Install Bootstrap
npm install --save bootstrap
Include bootstrap.css in the index.js
import 'bootstrap/dist/css/bootstrap.css';
React keys are useful when working with dynamically created components or when your lists are altered by the users. Setting the key value will keep your components uniquely identified after the change.
import React from 'react'; class App extends React.Component { constructor() { super(); this.state = { users:[ { name: 'Folau', id: 1 }, { name: 'Lisa', id: 2 } ] } } render() { return ( <div> <div> {this.state.data.map((user, i) => <h4>My name is {user.name}<h4/> )} </div> </div> ); } }
Itâs strongly recommended that you assign proper keys whenever you build dynamic lists. If you donât have an appropriate key, you may want to consider restructuring your data so that you do.
If no key is specified, React will present a warning and use the array index as a key by default. Using the array index as a key is problematic when trying to re-order a listâs items or inserting/removing list items. Explicitly passing key={i}
silences the warning but has the same problems as array indices and is not recommended in most cases.
Keys do not need to be globally unique; they only need to be unique between components and their siblings.
Install router
npm install –save react-router-dom
yarn add react-router-dom
In the index.js
import { BrowserRouter as Router, Route } from âreact-router-domâ; ReactDOM.render( <Providerstore={createStore(allReducers, applyMiddleware(thunk))}> <React.StrictMode> <Router> <Routepath=â/âexactcomponent={Home}/> <Routepath=â/loginâexactcomponent={Login}/> <Routepath=â/signupâexactcomponent={Signup}/> <Routepath=â/profileâexactcomponent={ProfilePage}/> <Routepath=â/aboutâexactcomponent={AboutPage}/> <Routepath=â/photosâexactcomponent={PhotosPage}/> <Routepath=â/friendsâexactcomponent={FriendsPage}/> </Router> </React.StrictMode> </Provider>, document.getElementById(ârootâ) );
Install redux
npm install react-redux
Add redux to index.js file
React Redux provides <Provider/>, which makes the Redux store available to the rest of your app
import React from 'react' import ReactDOM from 'react-dom' import thunk from 'redux-thunk' import {createStore, applyMiddleware} from 'redux' import { Provider } from 'react-redux' import store from './store' import allReducers from './redux/reducers/allReducer.js' import App from './App' const rootElement = document.getElementById('root') ReactDOM.render( <Provider store={createStore(allReducers, applyMiddleware(thunk))}> <App /> </Provider>, rootElement )
Connect Component to store
React Redux provides a connect function for you to connect your component to the store.
import React, { Component } from 'react'; import { connect } from 'react-redux'; import LandingPage from './landing_page'; import Header from './header/header'; import Posts from './post/posts'; import LeftPanel from './left_panel'; import RightPanel from './right_panel'; class Home extends Component { constructor(props) { super(props); this.state = { userAuthenticated: localStorage.getItem('userUuid') ? true : false } } componentDidMount() { } render() { return ( <div> <Header /> <main> <div className="main-wrapper pt-80"> <div className="container"> <div className="row"> <LeftPanel page="HOME"/> <Posts page="HOME"/> <RightPanel/> </div> </div> </div> </main> </div> ) } } const mapStateToProps = (state) => { return state; } export default connect(mapStateToProps, { })(Home);
Access data from the store
import React, { Component } from 'react'; import {connect} from 'react-redux'; import { loadNotifications } from '../redux/actions/NotificationAction'; class RecentNotifications extends Component { constructor(props) { super(props); console.log("RecentNotifications constructor()"); this.state = { } } componentDidMount(){ } render() { console.log("RecentNotifications render()"); console.log(this.props.ntcsState.notificationInfo.content); let listOfNotifications = {}; if(this.props.ntcsState.notificationInfo.content!==undefined && this.props.ntcsState.notificationInfo.content!==null){ listOfNotifications = this.props.ntcsState.notificationInfo.content.map((notification) => <li key={notification.uuid} className="unorder-list"> {/* profile picture end */} <div className="profile-thumb"> <a href="/"> <div className="profile-thumb-small"> <img src={notification.createdBy.profileImageUrl} alt="profile" /> </div> </a> </div> {/* profile picture end */} <div className="unorder-list-info"> <h3 className="list-title"><a href="/">Any one can join with us if you want</a></h3> <p className="list-subtitle">5 min ago</p> </div> </li> ); }else{ listOfNotifications = <li className="unorder-list"></li> } return ( <div className="card widget-item"> <h4 className="widget-title">Recent Notifications</h4> <div className="widget-body"> <ul className="like-page-list-wrapper"> {listOfNotifications} </ul> </div> </div> ) } } const mapStateToProps = (state) => { return state; } export default connect(mapStateToProps,{ loadNotifications: loadNotifications })(RecentNotifications);
You can access data from the store by connecting to the store and using props like this.props.ntcsState.
NotificationReducer.js
const notificationInitialState = { notificationInfo: {} }; let notificationInfo; export const NotificationStateReducer = (state = notificationInitialState, action) => { switch (action.type) { case 'LOAD_NOTIFICATIONS': notificationInfo = action.payload; //console.log("LOAD_NOTIFICATIONS"); //console.log(notificationInfo.data); return Object.assign({}, state, { notificationInfo: notificationInfo.data }); default: return state; } }
NotificationAction.js
import NotificationAPI from '../../api/notification'; export const loadNotifications = () => { let userUuid = localStorage.getItem("userUuid"); let pageSize = 10; let pageNumber = 0; //console.log("LOAD_NOTIFICATIONS"); return async function(dispatch, getState) { const response = await NotificationAPI.getNotifications(userUuid, pageSize, pageNumber); dispatch({ type: "LOAD_NOTIFICATIONS", payload: response }); }; };
Trigger actions in the store
import React, { Component } from 'react'; import Avatar from '../images/avatar.png'; import UserAPI from '../api/user'; import { connect } from 'react-redux'; import { setUserSession } from '../redux/actions/UserAction'; class ProfileUpdate extends Component { constructor(props) { super(props); this.state = { profile: { firstName: "", lastName: "", email:"", phoneNumber:"", gender: "", maritalStatus: "", profileImageUrl: props.userState.userProfileImageUrl!== null ? props.userState.userProfileImageUrl : Avatar, coverImageUrl: props.userState.userCoverImageUrl!== null ? props.userState.userCoverImageUrl : Avatar, uploadImage:"" } } this.loadProfile = this.loadProfile.bind(this); this.handleInputChange = this.handleInputChange.bind(this); this.updateProfile = this.updateProfile.bind(this); } componentDidMount() { } handleInputChange(event) { console.log("handleInputChange"); const target = event.target; const value = target.type === 'checkbox' ? target.checked : target.value; const name = target.name; if(name==="profileImageFile"){ console.log("upload profile image"); const formData = new FormData(); formData.append( "file", event.target.files[0], event.target.files[0].name ); let uuid = localStorage.getItem("userUuid"); UserAPI.uploadProfileImage(uuid, formData) .then(response => { console.log("response"); console.log(response.data); let user = response.data; this.setState({profile:user},()=>{}); this.props.setUserSession(user); }).catch(error => { console.log("error"); console.log(error.response.data); }); return; } if(name==="coverImageFile"){ console.log("upload cover image"); const formData = new FormData(); formData.append( "file", event.target.files[0], event.target.files[0].name ); let uuid = localStorage.getItem("userUuid"); UserAPI.uploadCoverImage(uuid, formData) .then(response => { console.log("response"); console.log(response.data); let user = response.data; this.setState({profile:user},()=>{}); this.props.setUserSession(user); }).catch(error => { console.log("error"); console.log(error.response.data); }); return; } let updatedState = { profile: this.state.profile } updatedState['profile'][name] = value; console.log("updated state"); console.log(updatedState); this.setState(updatedState, function () { //console.log(this.state); }); } loadProfile(event){ let uuid = localStorage.getItem("userUuid"); UserAPI.getProfile(uuid) .then(response => { console.log("response"); console.log(response.data); let user = response.data; this.setState({profile: user}); this.props.setUserSession(user); }).catch(error => { console.log("error"); console.log(error.response.data); }); } updateProfile(event){ console.log(this.state.profile); UserAPI.updateProfile(this.state.profile) .then(response => { console.log("response"); console.log(response.data); let user = response.data; this.setState({profile: user}); this.props.setUserSession(user); }).catch(error => { console.log("error"); console.log(error.response.data); }); } render() { return ( <div className="col-lg-2 col-md-3 d-none d-md-block"> <div className="profile-edit-panel"> <button onClick={this.loadProfile} className="btn-adda edit-btn" data-toggle="modal" data-target="#profileUpdateModal" >edit profile</button> </div> <div className="modal fade in" id="profileUpdateModal" aria-labelledby="profileUpdateModal"> <div className="modal-dialog"> <div className="modal-content"> <div className="modal-header"> <h5 className="modal-title">Update Profile</h5> <button type="button" className="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">Ă</span></button> </div> <div className="modal-body custom-scroll"> <div className="row"> <div className="col-sm-12 text-center"> <img src={this.state.profile.profileImageUrl} className="rounded" alt="..."/> </div> </div> <div className="row"> <div className="col-sm-6 offset-sm-3"> <div className="form-group text-center"> <input type="file" className="form-control-file" name="profileImageFile" onChange={this.handleInputChange} /> </div> </div> </div> <div className="row"> <div className="col-sm-12 text-center"> <img src={this.state.profile.coverImageUrl} className="rounded" alt="..."/> </div> </div> <div className="row"> <div className="col-sm-6 offset-sm-3"> <div className="form-group text-center"> <input type="file" className="form-control-file" name="coverImageFile" onChange={this.handleInputChange} /> </div> </div> </div> <div className="row"> <div className="col-sm-6"> <div className="form-group"> <label>First Name</label> <input type="text" className="form-control" aria-describedby="firstNameHelp" placeholder="Enter first name" name="firstName" onChange={this.handleInputChange} value={this.state.profile.firstName}/> </div> </div> <div className="col-sm-6"> <div className="form-group"> <label>Last Name</label> <input type="text" className="form-control" aria-describedby="lastNameHelp" placeholder="Enter last name" name="lastName" onChange={this.handleInputChange} value={this.state.profile.lastName} /> </div> </div> </div> <div className="row"> <div className="col-sm-6"> <div className="form-group"> <label>Email</label> <input type="email" className="form-control" aria-describedby="emailHelp" placeholder="Enter email" name="email" onChange={this.handleInputChange} value={this.state.profile.email} /> </div> </div> <div className="col-sm-6"> <div className="form-group"> <label>Phone</label> <input type="tel" className="form-control" aria-describedby="phoneNumberHelp" placeholder="Enter phone number" name="phoneNumber" onChange={this.handleInputChange} value={this.state.profile.phoneNumber} /> </div> </div> </div> <div className="row"> <div className="col-sm-6"> <div className="form-group"> <label>Gender</label> <select className="form-control" name="gender" onChange={this.handleInputChange} value={this.state.profile.gender}> <option value="MALE">Male</option> <option value="FEMALE">Female</option> <option value="TRANGENDER">Transgender</option> </select> </div> </div> <div className="col-sm-6"> <div className="form-group"> <label>Marital Status</label> <select className="form-control" name="maritalStatus" onChange={this.handleInputChange} value={this.state.profile.maritalStatus}> <option value="SINGLE">Single</option> <option value="MARRIED">Married</option> </select> </div> </div> </div> </div> <div className="modal-footer"> <button type="button" className="btn-adda post-share-btn" data-dismiss="modal">cancel</button> <button onClick={this.updateProfile} type="button" data-dismiss="modal" className="btn-adda post-share-btn">Update</button> </div> </div> </div> </div> </div> ) } } const mapStateToProps = (state) => { //console.log("home all states", state) return state; } export default connect(mapStateToProps, { setUserSession: setUserSession })(ProfileUpdate);
You use props to call actions like this
this.props.setUserSession(user);