Learning React with Typescript - reactjs

I have used observables in Angular, but I am having an issue in finding good examples on how to uses observables with React using Javascript. My end goal is to use hard coded data in a service.ts example
import { of, Observable } from 'rxjs';
export interface Volunteer {
firstName: string;
lastName: string;
totalHoursLogged: number;
}
const tempVolunteers: Volunteer[] = [
{ firstName: 'John', lastName: 'Smith', totalHoursLogged: 85 },
];
export const getAllVolunteers = (): Observable<Volunteer[]> => of(tempVolunteers);
I want to make a list component to build the mock data as it is push from the observable.
I would export the react component into the App.tsx and display the
mock data
If anyone knows of good resources or has any advice, please let me know.
Thanks

You can try this example (without Typescript for simplicity):
source file:
import { of } from 'rxjs';
const tempVolunteers = [
{ firstName: 'John', lastName: 'Smith', totalHoursLogged: 85 },
];
export const getAllVolunteers = of(tempVolunteers);
React app file:
import React, { Component } from 'react';
import {
getAllVolunteers,
} from '../RxJS';
class App extends Component {
state = {
list: [],
};
componentDidMount() {
getAllVolunteers.subscribe((item) => {
this.setState({
list: [...this.state.list, ...item],
});
});
}
render() {
return (
<div>
<ul>
{ this.state.list
.map(({ firstName, lastName, totalHoursLogged }) =>
(
<div key={lastName}>
{`${firstName} ${lastName} - ${totalHoursLogged}`}
</div>
),
)
}
</ul>
</div>
);
}
}
export default App;

Related

Cannot update during an existing state transition without any setState in render()

I am learning react and I encountered this error and could not find the solution.
The error I recieve is:
Cannot update during an existing state transition (such as within
render). Render methods should be a pure function of props and
state. at clientsDisplay
(http://localhost:3000/static/js/main.chunk.js:672:39) at div at App
(http://localhost:3000/static/js/main.chunk.js:163:5)
The problem is in this code:
import React from 'react'
import Client from './Client/Client'
const clientsDisplay=(props)=>props.clientsArray.map(
(client,index) =>{
return <Client
clientName={client.name}
clientProjDeadline={client.deadline}
clickDel={props.clientDel(index)}
key={client.id}
changed={event=>props.changed(event,client.id)}
len={props.clArraylength}
/>}
)
export default clientsDisplay
The main component which contains the render function looks like this:
import appCss from './App.module.css';
import React,{Component} from 'react';
import ClientsDisplay from './components/ClientHandle/ClientsDisplay';
class App extends Component{
state = {
userName:'Julian',
showPara: false,
clientsArray: [
{
id:"001",
name: "Max",
deadline: "2021/05/17"
},
{
id:"002",
name: "James",
deadline: "2021/12/06"
},
{
id:"003",
name: "Johnny",
deadline: "2021/07/21"
}
]
}
deleteClient = (delClientIndex)=>{
let clientsArrayCopy=[...this.state.clientsArray]
clientsArrayCopy.splice(delClientIndex,1)
this.setState({
clientsArray:clientsArrayCopy
})
}
valueChanger = (event)=>{
this.setState({
userName: event.target.value
})
}
valueChangerClient = (event,id)=>{
let targetInd=this.state.clientsArray.findIndex(elem=>elem.id===id)
let changedClientArray=[...this.state.clientsArray]
changedClientArray[targetInd].name=event.target.value
this.setState({
clientsArray:changedClientArray
})
}
togglePara = ()=>{
this.setState({
showPara: !this.state.showPara
})
}
render(){
let clientArraylength=this.state.clientsArray.length
return(
<div className={appCss.App}>
<ClientsDisplay
clientsArray={this.state.clientsArray}
changed={this.valueChangerClient}
clientDel={this.deleteClient}
clArrayLength={clientArraylength}/>
</div>
)
}
Currently you're actually calling props.clientDel on every render:
clickDel={props.clientDel(index)}
should be
clickDel={() => props.clientDel(index)}

React/Mobx: TypeError: Cannot read property 'map' of undefined

What I'm trying to do;
Pull data from API and insert it into a mobx store called UserStore
Retrieve and display User Store Data in another component
This is my UserStore.ts
import { observable, action } from 'mobx';
import { getUser } from '../api/api';
export class UserStore {
#observable users: [{
firstName: string;
surName: string;
dateofBirth: string;
ethnicity: string,
maritalStatus: string,
}]
#action
loadUser = () => {
getUser().then(users => this.users = users)
console.log(this.users)
}
}
This is my Api call to retrieve data.ts
This backend API is running as a separate project on Visual Studio, I have checked it is working and I am able to receive a 200 response when requesting through postman.
export const getUser = ():Promise<[{ firstName: string; surName: string; dateofBirth: string; ethnicity: string; maritalStatus: string; }]> => {
return fetch('https://localhost:5001/api/personalInfo/1').then(res => res.json())
}
This is my RootStateContext so that I am able to use the Store everywhere in the project
import React from 'react';
import { UserStore } from '../stores/UserStore';
type RootStateContextValue = {
userStore: UserStore;
};
const RootStateContext = React.createContext<RootStateContextValue>({} as.
RootStateContextValue);
const userStore = new UserStore();
export const RootStateProvider: React.FC<React.PropsWithChildren<{}>> = ({ children }) => {
return <RootStateContext.Provider value={{ userStore }}> {children}
</RootStateContext.Provider>;
};
export const useRootStore = () => React.useContext(RootStateContext);
This is my index.tsx
import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter } from 'react-router-dom';
import reportWebVitals from './reportWebVitals';
import App from './App';
import { RootStateProvider } from './context/RootStateContext';
import './css/main.css';
ReactDOM.render(
<BrowserRouter basename={'myCareer'}>
<RootStateProvider>
<App />
</RootStateProvider>
</BrowserRouter>,
document.getElementById('root'),
);
This is my PersonalInformation_Card.tsx where I am trying to map the data from UserStore and display it
export default function PersonalInformation_Paper() {
const { userStore } = useRootStore();
return useObserver(() => (
<>
<div>
{userStore.users.map((user) => {
return (
<option key={user.firstName} value={user.firstName}>
{user.firstName}
</option>
);
})}
</div>
</>
));
}
In this page I keep getting the error; TypeError: Cannot read property 'map' of undefined. I think this is because the API call is not being executed, and the store is not being populated so there is nothing to map, however I am not sure. Can anyone help?
Thanks
*Edit to show console.log(userStore) as requested
*Edit to show console.log(userStore) when changing
#observable users: [{
firstName: string;
surName: string;
dateofBirth: string;
ethnicity: string,
maritalStatus: string,
}]
Into
#observable users = [{
firstName: '',
surName: '',
dateofBirth: '',
ethnicity: '',
maritalStatus: '',
}]
try this:
export default function PersonalInformation_Paper() {
const { userStore } = useRootStore();
return useObserver(() => (
console.log(userStore)
<>
<div>
{userStore.users && userStore.users!=undefined ? userStore.users.map((user) => {
return (
<option key={user.firstName} value={user.firstName}>
{user.firstName}
</option>
);
}) : []}
</div>
</>
));
}
When you initialize your Store users implicitly set to undefined. Even if you call api later the first render will fail because of it.
You need to initialize them with empty array or check for existence:
type User = { firstName: string; surName: string; dateofBirth: string; ethnicity: string, maritalStatus: string, };
export class UserStore {
// Like that
#observable users: Array<User> = [];
// ...
}
or check that users array exists inside your component:
export default function PersonalInformation_Paper() {
const { userStore } = useRootStore();
return useObserver(() => (
<>
{userStore.users ?
<div>
{userStore.users.map((user) => {
return (
<option key={user.firstName} value={user.firstName}>
{user.firstName}
</option>
);
})}
</div>
: 'Loading...'}
</>
));
}

Create redux reducer to change state nested object value

I am new to React-Redux and i am struggling with a proof-of-concept project (list of movies) in order to learn the basics of redux. I am quite good at React, but Redux is another deal...
So, in my repo and specifically in Smart Component MoviesList.jsx (https://bitbucket.org/vforvaios/react-movie-app/src/05a6241eff3a1896eca91bb1800e8e017f8b569a/src/MoviesList.jsx?at=feature%2Fadd_redux&fileviewer=file-view-default) i am wondering how to dispatch the correct action to change the rating (increment, decrement) in each SingleMovieItem (https://bitbucket.org/vforvaios/react-movie-app/src/05a6241eff3a1896eca91bb1800e8e017f8b569a/src/SingleMovieItem.jsx?at=feature%2Fadd_redux&fileviewer=file-view-default) and what would be the correct reducer to achieve that. Could anyone help me with this?
In other words, to show a real example of what i mean...
Lets say that there is a main App and we make use of the
<Provider store={store}>
<App />
</Provider>
And then the App contains the following:
import React, { Component } from 'react';
import MoviesList from './MoviesList'
class App extends Component {
render() {
return (
<div>
<MoviesList />
</div>
)
}
}
export default App;
And then, the MovieList.jsd contains the following:
import React, { Component } from 'react';
import { addRating } from './actions';
import SingleMovieItem from './SingleMovieItem';
import { connect } from "react-redux";
const mapStateToProps = state => {
return {
allMovies: state
};
};
const mapDispatchToProps = (dispatch) => {
return {
onIncrement: (id) => dispatch(addRating(id))
};
};
class MovieList extends Component {
constructor(props) {
super(props);
}
handleIncrement = id => {
}
render() {
return (
<div>
<ul className="moviesList">
{this.props.allMovies.movies.map(movie => {
return (
<SingleMovieItem
key={movie.id}
movie={movie}
onIncrement={this.handleIncrement(movie.id)} />
);
})}
</ul>
</div>
)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(MovieList);
What would be the reducer to increment rating of each singlemovie
if initialState is
const initialState = {
movies: [
{
id: "1",
title: "Harry Potter 1",
url: "harry-1.jpg",
rating: "2"
},
{
id: "2",
title: "Harry Potter 2",
url: "harry-2.jpg",
rating: "3"
}
]
};
Thanks in advance.

Javascript map method doesn't work in reactjs project

I am completely new at react and I am following a tutorial. For this tutorial I have the following component called user-list.js:
import React, {Component} from 'react';
import {bindActionCreators} from 'redux';
import {connect} from 'react-redux';
class UserList extends Component {
createListItems() {
return this.props.users.map((user) => {
return (
<li>{user.first}</li>
);
});
}
render() {
return (
<ul>
{this.createListItems()}
</ul>
);
}
}
function mapStateToProps(state) {
return {
users: state.users
};
}
export default connect(mapStateToProps)(UserList);
And here is my reducer-users.js
export default function() {
return [
{
id: 1,
first: 'Bucky',
last: 'Roberts',
age: 71,
description: 'Bucky is a React developer anbd Youtuber',
thumbnail: 'http://i.imgur.com/7yUvePI.jpg'
},
{
id: 2,
first: 'Joby',
last: 'Wasilenko',
age: 27,
description: 'Joby loves the Packers, cheese and turtles',
thumbnail: 'http://i.imgur.com/52xRlm8.jpg'
},
{
id: 3,
first: 'Madison',
last: 'Williams',
age: 24,
description: 'Madi likes her dog but it is really annoying.',
thumbnail: 'http://i.imgur.com/4EMtxHB.jpg'
}
]
}
And now I am getting an error in the console:
Uncaught TypeError: Cannot read property 'map' of undefined
I don't understand what I am doing wrong, I removed the map function and returned any other data and it works fine, except when it tries to map the data.
When using the ES6 syntax for React Components, in your constructor function you need to bind any custom bethods you define on your class.
add the following to your UserList definition
constructor(props) {
super(props);
this.createListItems = this.createListItems.bind(this);
}
and you should be good to go. If you don't like doing this, you can also revert to the React.createClass({}) method for creating your component class.
you should have something like this for your code to work correctly.
// user reducer file
const initialState = [
{
id: 1,
first: 'Bucky',
last: 'Roberts',
age: 71,
description: 'Bucky is a React developer anbd Youtuber',
thumbnail: 'http://i.imgur.com/7yUvePI.jpg'
},
{
id: 2,
first: 'Joby',
last: 'Wasilenko',
age: 27,
description: 'Joby loves the Packers, cheese and turtles',
thumbnail: 'http://i.imgur.com/52xRlm8.jpg'
},
{
id: 3,
first: 'Madison',
last: 'Williams',
age: 24,
description: 'Madi likes her dog but it is really annoying.',
thumbnail: 'http://i.imgur.com/4EMtxHB.jpg'
}
];
export default function usersReducer(state, action) {
return state;
}
// root reducer file
import { combineReducers } from 'redux';
import usersReducer from 'your path to users reducer file'
export default function rootReducer() {
return combineReducers({
users: usersReducer,
});
}
// store file
import { createStore } from 'redux';
import rootReducer from 'path to root reducer file';
const store = createStore(rootReducer());
export default store;

How does React context know I am referencing react-router to redirect me to another URL

For my component, I set a context
ManageCoursePage.contextTypes = {
router: PropTypes.object
};
How does my class method know that I am referencing react router to automatically redirect me to another URL?
this.context.router.push('/courses');
Here is my component code:
import React, { PropTypes } from 'react';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import * as courseActions from '../../actions/courseActions';
import CourseForm from './courseForm';
class ManageCoursePage extends React.Component {
constructor(props, context) {
super(props, context);
// set up local state
this.state = {
errors: {},
course: Object.assign({}, this.props.course)
};
this.updateCourseState = this.updateCourseState.bind(this);
this.saveCourse = this.saveCourse.bind(this);
}
updateCourseState(event) {
const field = event.target.name;
let course = this.state.course;
course[field] = event.target.value;
return this.setState({
course: course
});
}
saveCourse(event) {
event.preventDefault();
this.props.actions.saveCourse(this.state.course);
this.context.router.push('/courses');
}
render() {
return (
<CourseForm
allAuthors={ this.props.authors }
onChange={ this.updateCourseState }
onSave={ this.saveCourse }
course={ this.state.course }
errors={ this.state.errors }
/>
);
}
}
ManageCoursePage.propTypes = {
// myProp: PropTypes.string.isRequired
course: PropTypes.object.isRequired,
authors: PropTypes.array.isRequired,
actions: PropTypes.object.isRequired
};
// Pull in the React Router context so router is available on this.context.router
// basically a global variable to make it easy for other components to get data easily
ManageCoursePage.contextTypes = {
router: PropTypes.object
};
function mapStateToProps(state, ownProps) {
// empty course
let course = {
id: "",
watchHref: "",
title: "",
authorId: "",
length: "",
category: ""
};
const authorsFormattedForDropDown = state.authors.map(author => {
return {
value: author.id,
text: author.firstName + " " + author.lastName
};
});
return {
course: course,
authors: authorsFormattedForDropDown
};
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(courseActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(ManageCoursePage);
Very interesting question. :)
It works because router is defined as a childContext type in the react-router library. getChildContext will make this accessible in inside the application if you map contextTypes in a component.
This is helpful in many ways to avoid deeply passing the props from a parent component to deep child component.
Refer this in react-router library https://github.com/reactjs/react-router/blob/master/modules/RouterContext.js#L36
And also the documentation https://facebook.github.io/react/docs/context.html

Resources