I have a problem when I wanna get data from state, in console.log appears 2 values. I want remove the empty value, but I've run out of ways. How to remove an empty value?
class DetailOrderTracking extends Component {
constructor(props) {
super(props)
this.state = {
data: []
}
}
componentDidMount = async () => {
const { query } = this.props.router;
var getOrderTrackings = await OrderTrackingRepository.getOrderTracking(query.numberbill, query.courier);
if (getOrderTrackings.ordertracking.status.code == 200) {
var getManifest = getOrderTrackings.ordertracking.result.manifest;
this.setState({ data: getManifest });
}
}
render() {
const { data } = this.state;
console.log(data) // will print 2 values, first condition is empty value, and second condition has values (an example is in the image above)
return (
<div/>
)
}
}
This should do it:
render() {
const { data } = this.state;
if (data.length > 0){
console.log(data)
}
return (
<div/>
)
}
Related
I have a React component with multiple dynamic input fields with different data types. I want to save the input values in state (answers) like this:
{ [id]: value }
Example of possible data output:
[
{
'72ebbdc4-8001-4b53-aac0': 'John doe'
},
{
'dd3179c1-90bc-481c-a89e':
'5b6d2f55-8ed0-4f76-98e69'
},
{
'5acff3c7-02f8-4555-9232': 4
},
{
'877817a8-6890-464b-928e': false
},
{
'69e11e5a-613f-46ac-805d': []
},
{
'0bb9c2f3-eda7-4e96-90f6': [
'ad9d4c72-0972764cf9b71c42',
'da788b55-3b68-a9c669c0ec1a'
]
},
{
'e9c2196f-871f-25e6efb2551f': '2020-12-23'
},
];
My React component is as follows. The InputField is a switch based on the questions type. When an input changes updateState is called and this.state.answers is updated. All of the question need to be filled in before the users can navigate to the next screen -> this.state.answeredAllQuestions.
export default class EditComponent extends Component {
state = {
questions: [],
answers: [],
answeredAllQuestions: false
};
async componentDidMount() {
await this.fillQuestions();
}
// I think need a working alternative for this part
componentDidUpdate() {
if (!this.state.answeredAllQuestions) {
this.checkRequiredQuestions();
}
}
fillQuestions = async () => {
const {
response: { questions }
} = await getQuestions();
// Turn questions from api into answers -> [key]:value
const answers = questions.map(el => {
return { [el.uuid]: el.value };
});
this.setState({
questions,
answers
});
};
checkRequiredQuestions = async () => {
const { answers } = this.state;
if (answers) {
const values = answers.map(x => Object.values(x)[0]);
if (
values.every(answer => {
(answer.required && answer !== null) || answer !== '';
})
) {
this.setState({ answeredAllQuestions: true });
} else {
this.setState({ answeredAllQuestions: false });
}
}
};
updateState = (value, id, nestedId) => {
const { answers } = this.state;
if (answers) {
// Check if answer already exists in the state, if so then replace it
this.setState({
answers: this.state.answers.map(el =>
Object.keys(el)[0] === id ? { [id]: value } : el
)
});
} else {
this.setState({
answers: [{ [id]: value }]
});
}
};
render() {
const { questions, answers } = this.state;
return (
<View>
<FlatList
data={questions}
renderItem={({ item: question }) => (
<View key={question.id}>
<Text>{question.label}</Text>
<InputField
type={question.type}
answers={answers}
updateState={this.updateState}
question={question}
/>
</View>
)}
/>
</View>
);
}
}
The big problem I have with this code is that when all input fields are filled in, the this.state.answeredAllQuestions is set too true. But when the user then removes a value from an input field it won't update back to false.
I don't expect someone to fix my code, but I could really use some help at the moment.
if (values.every(answer =>
(answer.required && (answer !== null || answer !== '' )) || answer === ''))
If the answer is required you need to check if isn't an empty string.
Fixed by passing a HandleInput function to all the inputs components, that checks for every data type if true or false and puts this value in the state of the EditComponent.
I am trying to render the data (object) that comes through props. However, I have got the following error:
Uncaught TypeError: Cannot convert undefined or null to object Some how, I do not know why the data or object is null although the state of the data is updated during componentDidMount(). Would you help me why the data is null?
Please look class A to see how the data is consumed
class A extends React.Component {
state = {
data: null
};
componentDidMount() {
this.data = this.props.location.state.data;
this.setState({ data: this.props.location.state.data });
}
render() {
return (
<div>
{Object.keys(this.data).map((key, index) => (
<p key={index}> value is {this.data[key]}</p>
))}
hello
</div>
);
}
}
A.propTypes = {
data: PropTypes.object,
location: PropTypes.object
};
export default A;
Assume, this.data contains the data in the following format
{
id: 1,
userName: "ABDXY",
date: "01/12/2020",
time: "21:00"
}
this.data is not defined. You can access the data that is set in the state using this.state.data
Please ensure that this.props.location.state.data is not null
class A extends React.Component {
state = {
data: {}
};
componentDidMount() {
// this.data = this.props.location.state.data; => not required.
this.setState({
data: this.props.location.state.data
});
}
render() {
return ( <
div > {
Object.keys(this.state.data).map((key, index) => ( <
p key = {
index
} > value is {
this.state.data[key]
} < /p>
))
}
hello <
/div>
);
}
}
Get data from state instead of this.data since it will not trigger render when this.data will update. Also use {} as default value
class A extends React.Component {
state = {
data: {}
};
componentDidMount() {
const data = {
id: 1,
userName: "ABDXY",
date: "01/12/2020",
time: "21:00"
};
this.setState({ data });
}
render() {
const { data } = this.state;
return (
<div>
{Object.keys(data).map((key, index) => (
<p key={index}> value is {data[key]}</p>
))}
hello
</div>
);
}
}
export default A;
I have a REACT app which is basically a till for adding items to an order. I have my OrderSection which does most of the grunt work, including having a barcode scanner, and I have my Search component which is a child of OrderSection, and if someone clicks on a search result it passes that back up to OrderSection via a prop callback.
Now, this is what I initially had, but it had problems:
#autobind
class OrderSection extends React.Component {
constructor(props) {
super(props);
this.state = {
orderItems: [],
multiMatch: [],
};
}
async barcodeScanner(barcode) {
let response;
try {
response = await serverApi.getItemsFromBarcode(barcode);
} catch(e) {
return toast.error(e.message || e.toString());
}
let {items} = response;
if (items.length === 0) {
toast.info('no matching items found');
} else if (items.length === 1) {
this.addItem(items[0]);
} else {
// show results in the 'search' section
this.setState({multiMatch: items})
}
}
addItem(item) {
// doesn't really matter what happens here
}
async lookupAdd(no, code) {
try {
let {items} = await serverApi.getItems(no, code);
let item = items[0];
if (item) {
this.addItem(item);
} else {
}
} catch(e) {
toast.error(e.toString());
}
}
render() {
return (
<section>
// render items up here
<Search
onItemClick={this.lookupAdd}
results={this.state.multiMatch} />
</section>
)
}
}
#autobind
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [],
show: false // change to true to show the search
}
}
// code for updating search results on form submit
// updating this.state.searchResults
render() {
return (
<React.Fragment>
// form with search text input here
// render searchResults here
</React.Fragment>
)
}
componentWillReceiveProps(props) {
if (props.results.length) {
this.setState({searchResults: props.results, show: true});
}
}
}
Search.propTypes = {
onItemClick: PropTypes.func.isRequired,
results: PropTypes.array
};
The main issue here is how in OrderSection, in barcodeScanner, when I have multiple matches, I pass them down as a prop into Search, and then Search sees that prop and updates itself in the componentWillReceiveProps function.
I wasn't entirely happy with what was happening there -- it was actually fine most of the time, but there was some annoying unexpected behaviour of Search showing itself when the prop actually hadn't changed.
So I came up with the idea of passing a callback up from Search to OrderSection:
#autobind
class OrderSection extends React.Component {
constructor(props) {
super(props);
this.state = {
orderItems: []
};
}
async barcodeScanner(barcode) {
let response;
try {
response = await serverApi.getItemsFromBarcode(barcode);
} catch(e) {
return toast.error(e.message || e.toString());
}
let {items} = response;
if (items.length === 0) {
toast.info('no matching items found');
} else if (items.length === 1) {
this.addItem(items[0]);
} else {
// show results in the 'search' section
this.sendMultiMatchToSearch(items);
}
}
setSearchResultsFunc(func) {
this.sendMultiMatchToSearch = func;
}
addItem(item) {
// doesn't really matter what happens here
}
async lookupAdd(no, code) {
try {
let {items} = await serverApi.getItems(no, code);
let item = items[0];
if (item) {
this.addItem(item);
} else {
}
} catch(e) {
toast.error(e.toString());
}
}
render() {
return (
<section>
// render items up here
<Search
onItemClick={this.lookupAdd}
manuallySetResultsFunc={this.setSearchResultsFunc}
/>
</section>
)
}
}
#autobind
class Search extends React.Component {
constructor(props) {
super(props);
this.state = {
searchResults: [],
show: false // change to true to show the search
};
if (typeof this.props.manuallySetResultsFunc === "function") {
const func = (results) => {
this.setState({searchResults: results, show: true});
this.flash();
};
this.props.manuallySetResultsFunc(func);
}
}
render() {
return (
<React.Fragment>
// render searchResults here
</React.Fragment>
)
}
}
Search.propTypes = {
onItemClick: PropTypes.func.isRequired,
manuallySetResultsFunc: PropTypes.func
};
But I feel like this is probably bad react practice. It's producing the behavior I want but I think if a React expert looks at this they wouldn't like it.
Can I get some advice on the proper way to pass search results down to Search to trigger it, while still otherwise allowing the SEARCH element to control its own searchResults code
You're right in that you shouldn't have to 'intervene' in this way to modify how your state is updated. You should just set up your state and props and then things should take care of themselves.
Here are some straightforward approaches that I'd typically use:
1) From the OrderSection parent to conditionally render your Search only when there are items:
render() {
return (
<section>
{this.state.multiMatch && <Search
onItemClick={this.lookupAdd}
manuallySetResultsFunc={this.setSearchResultsFunc}
/>}
</section>
)
}
2) Within the <Search> child:
render() {
return (
<React.Fragment>
{this.state.searchResults && this.state.searchResults.map(result=> // map to SearchResults)}
</React.Fragment>
)
}
3) From the OrderSection parent pass in 'isShowing' as a prop:
render() {
const isShowing = !!this.state.multiMatch; // add other logic here if necessary
return (
<section>
<Search
onItemClick={this.lookupAdd}
isShowing={isShowing}
/>
</section>
)
}
Then in your Search, extract isShowing from props.
The idea is that you only need to update the state and the rendering should take care of itself.
I would introduce additional props to Search component showMultiMatch and onSearchClose and add showSearch to OrderSection component(which is set to true when you receive multiMatch and set to false in the onSearchClose handler). Remove componentWillReceiveProps and check condition this.props.showMultiMatch || this.state.show in the render function to render search conditionally.
I'm here to ask what's your idea to properly pass a CheckBox value to other screen?
Example, if a user checks a CheckBox then proceed to the Next Screen the value of the CheckBox should be displayed in that screen.
But in my code, my console.log gives an output of false(I don't understand why) and once I get to the next screen the state doesn't really pass because it's display is blank.
Here's my code
export default class tables extends Component {
constructor(props){
super(props)
this.state = {
...
check: {},
tbl_Merge: []
}
}
proceed_TO_Category = () => {
this.props.navigation.navigate('Category', {
userName : this.state.userName,
DineIn : this.state.DineIn,
tbl : this.state.tbl,
tbl_2nd : this.state.tbl_2nd,
tbl_Merge : this.state.tbl_Merge
});
console.log("CHECK ======> "+ this.state.tbl_Merge);
}
checkBox_Test = (table_NO) => {
const tbl_Merge = this.state.tbl_Merge;
const checkCopy = {...this.state.check}
if (checkCopy[table_NO]) {
checkCopy[table_NO] = false;
} else {
checkCopy[table_NO] = true;
}
this.setState({ check: checkCopy });
this.setState({ tbl_Merge: table_NO == this.state.tbl_Merge });
}
render() {
return(
<View>
....
<Flatlist
....
<CheckBox
value = { this.state.check[item.tbl_id] }
onChange = {() => this.checkBox_Test(item.tbl_id) }
/>
....
/>
....
<View>
<TouchableOpacity
onPress = {() => this.proceed_TO_Category()}>
<Text>Categories</Text>
</TouchableOpacity>
</View>
<View/>
)
}
}
Screen shot of Console.log in my proceed_TO_Category.
checkBox_Test = (table_NO) => {
const tbl_Merge = this.state.tbl_Merge;
const checkCopy = {...this.state.check}
if (checkCopy[table_NO]) {
checkCopy[table_NO] = false;
} else {
checkCopy[table_NO] = true;
}
this.setState({ check: checkCopy, tbl_Merge: table_NO == this.state.tbl_Merge }); // 1. Edit
}
Edit: We should not use setState consecutively. Because setState is async func. If you want to call a function after setState process with the help of callback function,setState(update, callback);
you defined tbl_Merge as an array, but while you set it to state, you set it as a boolean.
I am building a React app that - among other things - generates a random number when a button is clicked and then filters an array of JSON objects to only the one at the index of that random number (i.e. JSON[random]). Normally the app is supposed to re-render after the array of JSON objects is filtered, but for some reason, on the first time the button is clicked and a random is picked, it requires two clicks to update. From then on it updates as expected, with a new random rendering each time the button is clicked.
I'm not sure if the problem is coming from App.js or somewhere lower down. On the first click, it generates a new random and supposedly saves this to state, but fails to re-render right away. On subsequent clicks, things seem to update based on the previously-generated random, while a new random is put in the queue. I would prefer the this all happens in one go: click, generate random, save to state, update to reflect the new random à la JSON[random].
This might have something to do with the way I have implemented lifecycle methods, as I'm admittedly not sure of all the nuances of each and have just tried to use whichever ones seemed to do what I wanted. If you have any suggestions there, please let me know...
Thanks!
Here are the relevant files:
App.js - where the random is generated and stored when a new click is registered in Header.state.randomClicks
class App extends Component {
constructor(props){
super(props)
this.state = {headerLink: "", searchValue: "", random: 0, randomClicks: 0}
this.generateRandom = this.generateRandom.bind(this);
}
getLinkFromHeader = (link) => {
if (this.state.headerLink !== link) {
this.setState({
headerLink: link,
})
}
}
getSearchValueFromHeader = (string) => {
this.setState({
searchValue: string,
});
}
getRandomMax = (max) => {
this.setState({
randomMax: max,
})
}
getRandomClicks = (value) => {
this.setState({
randomClicks: value,
})
}
generateRandom(number) {
let random = Math.floor(Math.random() * number) + 1;
console.log("generateRandom = ", random)
return random
}
shouldComponentUpdate(nextProps, nextState) {
return this.state.randomClicks !== nextState.randomClicks;
}
componentWillUpdate() {}
componentDidUpdate(prevState) {
let randomClicks = this.state.randomClicks;
console.log("this.state.randomClicks: ", this.state.randomClicks)
// console.log("prevState: ", prevState)
// console.log("prevState.randomClicks = ", prevState.randomClicks)
// ^^ is this a bug ? ^^
let random = this.generateRandom(this.state.randomMax);
if (this.state.random !== random) {
this.setState({random: random})
}
}
render() {
return (
<div className="App background">
<div className="content">
<Header getLinkFromHeader={this.getLinkFromHeader} getSearchValueFromHeader={this.getSearchValueFromHeader} randomClick={this.randomClick} getRandomClicks={this.getRandomClicks}/>
<TilesContainer link={this.state.headerLink} searchValue={this.state.searchValue} getRandomMax={this.getRandomMax} random={this.state.random} randomClicks={this.state.randomClicks}/>
</div>
</div>
);
}
}
export default App
Header.js* - where the randomClick count is incremented each time RandomButton is clicked
class Header extends Component {
constructor(props){
super(props);
this.state = { selectorLink: "", searchValue: "", randomClicks: 0 }
this.randomClick = this.randomClick.bind(this);
}
getLinkFromSelector = (link) => {
this.setState({
selectorLink: link,
})
}
getSearchValue = (string) => {
this.setState({
searchValue: string,
})
}
shouldComponentUpdate(nextProps, nextState) {
console.log("this.state !== nextState: ", this.state !== nextState)
return this.state !== nextState;
}
componentDidUpdate(previousState){
if(this.state.selectorLink !== previousState.selectorLink) {
this.props.getLinkFromHeader(this.state.selectorLink);
}
this.props.getSearchValueFromHeader(this.state.searchValue);
this.props.getRandomClicks(this.state.randomClicks);
console.log("Header Did Update")
}
randomClick(){
this.props.randomClick;
this.setState({
randomClicks: this.state.randomClicks += 1,
});
}
render(){
return(
<div id="header" className="header">
<div className="title-div">
<div className="h1-wrapper title-wrapper">
<h1>Pokédex Viewer App</h1>
</div>
</div>
<PokedexSelector getLinkFromSelector={this.getLinkFromSelector}/>
<SearchBar getSearchValue={this.getSearchValue}/>
<button type="button" id="random-button" onClick={this.randomClick}>Random Pokémon</button>
<button type="button" id="show-all-button" onClick={this.showAllClick}>Show All</button>
</div>
)
}
}
export default Header
TilesContainer.js - where the random number from App is sent and the tiles list is filtered/re-rendered
class TilesContainer extends Component {
constructor(props){
super(props);
this.state = {
pokemon: [],
filteredPokemon: [],
randomMax: 0,
showDetails: false,
};
this.getPokemon = this.getPokemon.bind(this);
this.tiles = this.tiles.bind(this);
this.getPokemon(this.props.link);
}
getPokemon(pokedexLink) {
let link = "";
(pokedexLink === "")
? link = "https://pokeapi.co/api/v2/pokedex/national/"
: link = this.props.link;
fetch(link)
.then(response => response.json())
.then(myJson => {
let list = myJson['pokemon_entries'];
this.setState({
pokemon: list,
randomMax: list.length,
})
this.props.getRandomMax; // send randomMax to App
})
}
filterPokemon(string) {
if (string !== "") {
console.log("string: ", string)
string = string.toString().toLowerCase()
let filteredPokemon = this.state.pokemon.filter(pokemon => {
const name = pokemon.pokemon_species.name;
const nameStr = name.slice(0,string.length);
const number = pokemon.entry_number;
const numberStr = number.toString().slice(0, string.length);
return (this.state.random !== 0) ? number.toString() === string : nameStr === string || numberStr === string;
})
if (this.props.randomClicks !== 0) { // i.e. using a random
this.setState({
filteredPokemon: filteredPokemon,
})
} else {
this.setState({
filteredPokemon: filteredPokemon,
randomMax: filteredPokemon.length,
})
}
} else {
this.setState({
filteredPokemon: [],
randomMax: this.state.pokemon.length,
})
}
}
componentDidUpdate(prevProps, prevState) {
if (this.props.link !== prevProps.link) {
this.getPokemon(this.props.link)
}
if (this.props.searchValue !== prevProps.searchValue) {
this.filterPokemon(this.props.searchValue)
}
if (this.state.randomMax !== prevState.randomMax){
this.props.getRandomMax(this.state.randomMax);
}
if (this.props.random !== prevProps.random) {
console.log("TilesContainer random: ", this.props.random)
this.filterPokemon(this.props.random)
}
}
tiles() {
console.log("tiles() filteredPokemon: ", this.state.filteredPokemon)
console.log("tiles() searchValue: ", this.props.searchValue)
console.log("tiles() random: ", this.props.random)
if (this.state.pokemon.length > 0) {
if (this.state.filteredPokemon.length == 0 && this.props.searchValue === ""){
return (
this.state.pokemon.map(pokemon => (
<Tile key={pokemon.entry_number} number={pokemon.entry_number} name={pokemon.pokemon_species.name} url={pokemon.pokemon_species.url}/>
))
)
} else if (this.state.filteredPokemon.length > 0){
return (
this.state.filteredPokemon.map(pokemon => (
<Tile key={pokemon.entry_number} number={pokemon.entry_number} name={pokemon.pokemon_species.name} url={pokemon.pokemon_species.url}/>
))
)
}
}
}
render(){
return (
<div id="tiles-container"
className="tiles-container">
{this.tiles()}
</div>
)
}
}
export default TilesContainer
You should not use current state in setState and should not modify state directly. And you do no actually call this.props.randomClick and it is undefined. Change
randomClick(){
this.props.randomClick;
this.setState({
randomClicks: this.state.randomClicks += 1,
});
}
to
randomClick(){
if (typeof(this.props.randomClick) === 'function') this.props.randomClick();
this.setState(olState => ({
randomClicks: olState.randomClicks + 1,
}));
}
Also check your shouldComponentUpdate methods. They might be buggy or redundant. Looks like you prevent updating App when state.random changes. So every time you click the button you store the new random value but use the previous one. So for the initial render and for the first click you use random: 0.
And I guess that getRandomClicks should be setRandomClicks.