React setState wont re-render - javascript

I'm working on a self project React for front-end and node for back-end, part of the app is that when a user submits an image url it counts the entries and it updates in the server then it should re-render on the front-end to the screen. The problem is it doesn't re-render, i have console.log tested and everything works from the server side, the problem is in the setState in react wont re-render and i'm hoping any one help me understand why it is not working?
Here is the code related to my problem
class App extends Component {
constructor() {
super()
this.state = {
input: '',
imgUrl: '',
box: {},
route: 'signin',
isSignedIn: false,
user: {
id: '',
name: '',
email: '',
entries: 0,
joined: '',
},
}
}
loadUser = data => {
this.setState({
user: {
id: data.id,
name: data.name,
email: data.email,
entries: data.entries,
joined: data.joined,
},
})
}
onButtonSubmit = () => {
fetch('http://localhost:3001/image', {
method: 'put',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
id: this.state.user.id,
}),
})
.then(response => response.json)
.then(count => {
this.setState({ ...this.state.user, entries: count })
})
.catch(err => console.log(err))
}
}
render() {
return (
<div className="App">
<Navigation
isSignedIn={this.state.isSignedIn}
onRouteChange={this.onRouteChange}
/>
{this.state.route === 'home' ? (
<div>
<Rank
name={this.state.user.name}
entries={this.state.user.entries}
/>
<ImageLinkForm
onInputChange={this.onInputChange}
onButtonSubmit={this.onButtonSubmit}
/>
<FaceRecognition box={this.state.box} imgUrl={this.state.imgUrl} />
</div>
) : this.state.route === 'signin' ? (
<Signin loadUser={this.loadUser} onRouteChange={this.onRouteChange} />
) : (
<Register
loadUser={this.loadUser}
onRouteChange={this.onRouteChange}
/>
)}
</div>
)
}
this code is suppose to print the entries count on the screen but its not
this.setState({...this.state.user, entries: count})
here is the server side where entries gets updated and sent to the front-end
app.put('/image', (req, res) => {
const { id } = req.body
let found = false
database.users.forEach(user => {
if (user.id === id) {
found = true
user.entries++
return res.json(user.entries)
}
})
if (!found) {
res.status(400).json('not found')
}
})
here is the rank Component where entries gets printed
import React from 'react';
const Rank = ({ name, entries}) => {
return (
<div>
<div className='rank'>
{`${name} your current rank is...`}
</div>
<div className='white f1 '>
{entries}
</div>
</div>
);
}
export default Rank;
Thanks in advance.

I don’t see any use of doing ...this.state.user in setState So
Change
this.setState({...this.state.user, entries: count})
To
this.setState({entries: count})

Related

facing error of bad request even thought the query is same

Well i am trying to reduce the line of code at once refactoring the code
import React, { Component } from 'react';
import { Loader } from '../../components';
import './ProductListing.scss';
import { ProductCard } from '../../components';
import { productQuery } from '../../utls/queries';
export class ProductListing extends Component {
constructor(props) {
super(props);
this.state = {
loading: true,
products: [],
categoryId: '',
};
}
componentDidMount() {
const currentUrl = window.location.pathname;
const id = currentUrl.replace('/', '');
this.setState({ categoryId: id });
const newQuer = { ...productQuery };
const query = `
query{
categories {
name
products {
id,
name,
brand,
inStock,
gallery,
category,
prices {
amount,
currency {
label,
symbol
}
}
}
}
}
`;
console.log(query === productQuery);
console.log(productQuery);
fetch('http://localhost:4000', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify({
query,
}),
})
.then((response) => {
return response.json();
})
.then((data) => {
this.setState({
products: data.data,
loading: false,
});
});
}
render() {
if (this.state.loading === true) {
return <Loader />;
} else {
return (
<div>
<h2 className='page__listing__title'>
{this.state.categoryId[0].toUpperCase() +
this.state.categoryId.substring(1)}
</h2>
<div className='productlisting__page'>
{this.state.products.categories.map((item, index) => (
<div key={index}>
{item.name === this.state.categoryId ? (
<div className='product__listing__card'>
{item.products.map((product, i) => (
<ProductCard product={product} key={i} />
))}
</div>
) : (
''
)}
</div>
))}
</div>
</div>
);
}
}
}
export default ProductListing;
In the process of reducing code i see that the query is taking a lot of places so i decided to write it at separate place now i am importing it as productQuery when i console.log(productQuery===query) it says true but the place where i am using the query to fetch data i use productQuery it just give bad error i cant understand ...
if some one have much better idea i really like if you can suggest me much better ways by which i can reduce the lines of code
I think what's happening is you're accidentally destructuring the query you import, when you say
const newQuery = {...productQuery}
productQuery is simply a string (as proven by your console log that stays productQuery === query).
newQuery is an object that destructures the string, and trying to use that would likely result in a failure.

Updating redux state by a local state of checkbox items

there are similiar questions in stackoverflow but I I did not find what I was looking for.
I have a donorDonationForm which is a class componenet that connected to the redux state. The porpuse of that componenet is to collect inormation about a person that want to donate electronics items. At this point, I want to save those items in an array (maybe with an object in the future).
my redux state save the donor info and the reducer looks like this:
import {CHANGE_INPUT_FIELD} from '../utils/constants';
const initialStateInputs = {
// update the state
donorFields: {
name: '',
phone: '',
area: '',
yeshuv: '',
address: ''
// dateOfOffer: ''
},
donationFields: {
// donorID: '',
// vulonteerID: '',
type: [],
quantity: 1,
status: 'NOT_HANDLED',
comments: ''
// lastDateHandled: ''
}
// }, items: [ //need to add quantity
// {id: 1, name: "LAPTOP", isChecked: false, label: 'מחשב'},
// {id: 2, name: "HEADPHONES", isChecked: false, label: 'אוזניות'},
// {id: 3, name: "OTHER", isChecked: false, label: 'אחר'},
// ]
}
export const donorDonationInputsReducer = ( state = initialStateInputs, action={} ) => {
switch(action.type) {
case CHANGE_INPUT_FIELD:
return Object.assign( {}, state,
{
donorFields : {...state.donorFields,...action.payload},
donationFields: {...state.donationFields,...action.payload},
// items : {...state.items,...action.payload},
// isChecked: action.payload
})
default:
return state;
}
}
As you can see the items is commented by now, and I am managing the state of the item in a local state, and that how the comp looks like:
import React, {Component} from 'react';
import { connect } from 'react-redux';
import { setInputField } from '../actions/formAction';
import CheckBox from '../components/CheckBox/CheckBox';
import FormInput from '../components/FormInput/FormInput';
import {selectAreasOptions_2} from '../utils/constants';
import "./form.css";
const mapStateToProps = (state) => {
return {
donorFields: state.donorDonationInputsReducer.donorFields,
donationFields: state.donorDonationInputsReducer.donationFields
}
}
const mapDispatchToProps = dispatch => {
return {
onInputChange: event => {
const {name, value} = event.target;
dispatch(setInputField( { [name]:value} ) )
}
}
}
class donorDonationForm extends Component {
constructor() {
super();
this.state = {
items: [
{id: 1, name: "LAPTOP", isChecked: false, label: 'מחשב'},
{id: 2, name: "HEADPHONES", isChecked: false, label: 'אוזניות'},
{id: 3, name: "OTHER", isChecked: false, label: 'אחר'},
]
,
type: []
}
}
handleCheckChieldElement = (event) => {
let {items, type} = this.state;
let arr = [];
items.forEach(item => {
if (item.name === event.target.value) {
item.isChecked = event.target.checked;
// console.log(`item.name :${item.name }`);
// console.log(`event.target.value :${event.target.value}`);
// console.log(`event.target.checked :${event.target.checked}`);
}
})
items.map(item => item.isChecked ? arr.push(item.name) : null)
this.setState({items: [...items], type: [...arr]});
}
onButtonSubmit = (event) => {
console.log(this.props.donorFields);
event.preventDefault();
fetch('http://localhost:8000/api/donor', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
...this.props.donorFields
})
})
.then(response => response.json())
.then(resp => console.log(resp))
.catch( err => console.log(err) )
}
// componentDidUpdate(prevProps, prevState) {
// const {items, type} = this.state;
// // const type = [];
// if (prevState.items !== items) {
// console.log('items state has changed');
// items.map (item => item.isChecked ?
// this.setState({type: [...type,item.name]}) : null)
// // if (item.isChecked) { type.push(item.name) } ;
// console.log(type);
// }
// }
render() {
console.log(this.state.items);
console.log(this.state.type);
const { onInputChange } = this.props;
return (
<div>
<h1 className="pt4"> פרטי תורם</h1>
<form className=" black-80 pt2" >
<section className=" grid-container">
<FormInput
id="name"
name="name"
type="text"
onInputChange={onInputChange}
label="שם "
required
/>
<FormInput
id="phone"
name="phone"
type="tel"
onInputChange={onInputChange}
label="מספר טלפון "
required
/>
<FormInput
id="address"
name="address"
type="text"
onInputChange={onInputChange}
label="כתובת "
required
/>
<FormInput
id="yeshuv"
name="yeshuv"
type="text"
onInputChange={onInputChange}
label="עיר "
required
/>
<FormInput
id="comments"
name="comments"
onInputChange={onInputChange}
label="הערות "
required
/>
<FormInput
id="area"
name="area"
onInputChange={onInputChange}
label="איזור "
select={selectAreasOptions_2}
/>
{/* type */}
<div className="measure-narrow">
<label htmlFor="type" className="f5 b db mb2">מעוניין לתרום
<span className="normal black-60"> *</span>
</label>
{
this.state.items.map( (item, i) => {
return (
<CheckBox
key={i}
onChange={this.handleCheckChieldElement}
checked={ item.isChecked }
value= {item.name}
label = {item.label}
/>
);
})
}
</div>
</section>
<input type="submit" value="שלח"
className="b bg-light-blue pa2 hover pointer"
onClick={this.onButtonSubmit}
/>
</form>
</div>
);
}
}
export default connect(mapStateToProps, mapDispatchToProps)(donorDonationForm);
My main goal is that the type array - the final donation, will update the redux state before submitting this form. I tried with componentDidUpdate but didn't make it. What is the best way for tracking the checked items, updating the array and then update the type array which is the final donation in the redux state? should I do that in the onButtonSubmit method - before sending the data to the server (and thats way saving the looping over the items array for searching the checked elements) ?
Better approach would be do inside onButtonSubmit
Let me briefly explain the tasks:
inputChangeHandler to update this.state.items
Go with the final this.state.items value Array of items inside onButtonSubmit
After getting API response update the application level Redux state with Array of items.
Note: Dispatch the action. Reducer will update the Redux state. Following code will do this:
// Action
export const setItems = (data) => (dispatch) => {
dispatch({type: 'SET_ITEMS', payload: data})
}
// mapDispatchToProps
const mapDispatchToProps = (dispatch) =>
bindActionCreators(
{
setItems,
...others
},
dispatch
)
// onSubmitButton
onButtonSubmit = (event) => {
console.log(this.props.donorFields);
event.preventDefault();
fetch('http://localhost:8000/api/donor', {
method: 'post',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
},
body: JSON.stringify({
...this.props.donorFields
})
})
.then(response => this.props.setItems(response.json())) // will update the state.
.then(resp => console.log(resp))
.catch( err => console.log(err) )
}
// Reducer
export const donorDonationInputsReducer = ( state = initialStateInputs, action={} ) => {
switch(action.type) {
case CHANGE_INPUT_FIELD:
return Object.assign( {}, state,
{
donorFields : {...state.donorFields,...action.payload},
donationFields: {...state.donationFields,...action.payload},
// items : {...state.items,...action.payload},
// isChecked: action.payload
})
case SET_ITEMS:
return {
...state,
items: action.payload
}
default:
return state;
}
}
That's it.
Happy Coding :)

JS Validation input

I'm just starting my adventure in react.
I just want to do the number validation.
I found many solutions but none work. from here I took the solution Only numbers. Input number in React
I don't know further this is happening
this is the code with the added solution I found.
import React, { Component } from 'react';
import validator from 'validator';
export default class GetApp extends Component {
state = {
// creditId: '',
err: false,
name: '',
firstName: '',
lastName: '',
pesel: '',
productName: '',
value: ''
}
constructor(props) {
super(props);
this.state = {
creditId: ''
}
}
onChange = (e) => {
//this.setState({ creditId: e.target.value });
}
handleChange(evt) {
const creditId = (evt.target.validity.valid) ? evt.target.value : this.state.creditId;
this.setState({ creditId });
}
handleSubmit = event => {
event.preventDefault();
fetch(`http://localhost:8200/credit/getCredit/${this.state.creditId}`)
.then(res => {
if (res.ok) {
return res
}
}).then(res => res.json())
.then(data => {
this.setState({
err: false,
name: data.credit.name,
firstName: data.customer.firstName,
lastName: data.customer.lastName,
pesel: data.customer.pesel,
productName: data.product.productName,
value: data.product.value
})
})
.catch(err => {
console.log(err);
this.setState({
err: true
})
})
}
render() {
let content = null;
if (!this.state.err && this.state.creditId) {
content = (
<div>
<p>Name: {this.state.name}</p>
<p>First Name: {this.state.firstName}</p>
<p>Last Name: {this.state.lastName}</p>
<p>PESEL: {this.state.pesel}</p>
<p>Product Name: {this.state.productName}</p>
<p>Value: {this.state.value}</p>
</div>
)
}
return (
<form onSubmit={this.handleSubmit}>
<div className="container">
<h2>Get Credit</h2>
<label>Credit Number:</label>
<input type='text' name="creditId" value={this.state.creditId} pattern="[0-9]*" onInput={this.handleChange.bind(this)} />
<div>
<button type="submit">Submit</button>
<p>{this.state.err ? `Dont search: ${this.state.creditId}` : content}</p>
<div>
</div>
</div>
</div>
</form>
)
}
}
Thanks for help
You could also change the input type to number https://www.w3schools.com/tags/att_input_type_number.asp . This won't allow users to set anything else but numeric (0-9) characters and is a bit more strict.

Searching for elements and setting pagination only on these searched elements

I have this problem: for example, I will search for the phrase ab for 45 results. How to set pagination to include only 45 results, not all elements? He is currently looking for 45 items for me. But when I go to the other side, it includes all the elements for me, not just the ones I searched for.
class App extends Component {
constructor() {
super();
this.state = {
todos: []
};
}
searchTodo = debounce((query) => {
this.setState({
query
})
this.getTodo((query), 200)
})
getTodos = (number) => {
const params = {
expand: 'project',
'per-page': 20,
'page': number
}
axios({
url: 'https://jsonplaceholder.typicode.com/todos',
method: "GET"
})
.then(res => {
this.setState({
todos: res.data
});
})
.catch(error => {
console.log(error);
})
}
handlePage = (number) => {
this.getTodos(number);
}
render() {
return (
<div>
<Hello name={this.state.name} />
<p>
<SearchInput onChange={e => this.searchTodo(e)} />
</p>
<Pagination
itemsCountPerPage={20}
totalItemsCount={this.state.todos.length}
onChange={this.handlePage}
/>
</div>
);
}
}

Display options for react-bootstrap-typeahead when using apolloconsumer

I'm trying to display a list of options retrieved from a graphql query in react-bootstrap-typeahead.
I can see this.state.options is updating and my component is re-rendering so I see the new values in the console log. But I keep getting 'No matches found' in my typeahead dropdown.
My react component render method:
render() {
console.log('rendering', this.state.options);
return (
<ApolloConsumer>
{ client => (
<AsyncTypeahead
{...this.state}
labelKey="value"
minLength={3}
onSearch={e => this.handleSearch(e, client)}
placeholder="Search for options..."
renderMenuItemChildren={option => (
<LookupItem key={option.id} option={option} />
)}
/>
)}
</ApolloConsumer>
);
}
My handleSearch method:
async handleSearch(searchValue, client) {
this.setState(state => ({
...state,
isLoading: true,
}));
const { data, error } = await client.query({
query: LookupQuery,
variables: {
lookupValue: searchValue,
limit: 10,
},
fetchPolicy: 'network-only',
});
if (error) {
console.log('error', error);
} else {
const lookupValues = _.get(data, 'lookup', []);
this.setState(state => ({
...state,
isLoading: false,
options: lookupValues.map(item => ({ value: item.value, description: item.description })),
}));
}
}
LookupItem:
const LookupItem = ({ option }) => {
console.log('option', option);
return (
<div>
<span>{option.value}</span>
</div>
);
};
I hope that all makes sense. Thanks in advance - and apologies if this is something incredibly stupid!

Categories

Resources