function validate is supposed to return element if function getConjugation doesn't return error, the valid input is passed to getConjugation, but it just renders nothing in browser
import React from 'react'
import {getConjugation} from 'german-verbs'
import GermanVerbsDict from 'german-verbs-dict'
export default ({word})=>{
const renderVerbs =(w)=>{
return (
}
const validate = (w) =>{
try{
return(
<ul>
<li>{w.toLowerCase()}</li>
<li>{getConjugation(GermanVerbsDict, "haben",'PRASENS', 2,'S' )}</li>
<li>{getConjugation(GermanVerbsDict,"haben",'PRATERITUM', 1,'S' )}</li>
<li>{getConjugation(GermanVerbsDict, "haben",'PERFEKT', 1,'S', "HABEN")}</li>
</ul>)
}catch {return false}
}
return (
<div className="verb">{validate(word)}</div>
)
}
You may try 3 things
Complete the renderVerbs function since now is missing a bracket.
This part of code seems to be exported out. Have you added this component to where it suppose to be?
Check whether validate function is actually been called. Here is what I will do.
const validate = (w) =>{
console.log("the word is: ", w);
return(
<ul>
<li>{w.toLowerCase()}</li>
//if your function return an array
YourArray.map(elem => (
<li>{elem}</li>
))
</ul>)
}
}
return (
<div className="verb">{validate(word)}</div>
)
If it does work, add one extra block code each at a time until it comes to your original code mentioned here. Hope you may find which part is buggy in this process. Good luck.
Related
I am facing difficulties rendering array on screen when I navigate back to the component. I have been trying and searching since morning for the solution but no luck. Please let me explain
I have a react component called ShowTags.tsx that contains a callback function handleTagReading() which return strings every second (async behavior).
When the string tag arrives I am storing it in [storeTags] state array and in the return method, using map function to iterate and display the tags.
The Sample code
export default function ShowTags() {
//array to store string tags
const [storeTags, setStoreTags] = useState<String[]>([]);
//callback method
const handleTagReading = (tag) => {
console.log("print the tag" + tag) // this line runs everytime
setStoreTags(storeTags => [...storeTags!, tag]);
}
return (
<>
/*This component contains a button, on button click it runs the loop method which runs
the parent callback method*/
<ReadTags parentCallback = {handleTagReading} />
<div className="container">
{storeTags && storeTags!.map((item, index) => {
return (
<div>
{item}
</div>)
})}
</div>
</>
)
}
This code works perfectly as long as I am on The ShowTags component for the first time. The moment I navigate to a different component and come back, the map method shows nothing. But console.log() still runs showing the tags coming from a callback.
I have tried using the useEffect(), cleanup(), boolean states variables, non state variables but the component does not render when switching back to ShowTags component.
Please help me out here and let me know if you need more information
UPDATE -edit
For simplicity I said async behavior but actually I am using Serial USB API to read data from RFID reader (external hardware device connected via USB)
The ReadTags() component contains lot of code but I am sharing the necessary bits
export default function ReadTags(props) {
//send diffenent commands to reader on button press
async function sendSerialLine() {
try{
await writer.write(dataToSend);
}catch(e){
console.log("the write error")
setShowConnect(true)
}
}
//The listenToPort method runs continuously when the RFID reader gets connected via Serial USB and does not stop.
async function listenToPort(){
/*serial USB API implementation*/
textDecoder = new TextDecoderStream();
readableStreamClosed = port.readable.pipeTo(textDecoder.writable);
reader = textDecoder.readable.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) {
reader.releaseLock();
break;
}
//parent callback
props.parentCallback(value);
}}
return (
<div>
<p onClick={() => sendSerialLine()} className="btn btn-primary">Start Reading</p>
</div>
)
}
Try to use useCallback with handleTagReading
I've been trying to work out whats going in the below code all day and after searching through stack over flow and you-tube, I still cant figure out whats wrong with my code.
Essentially I have a user document stored on firebase firestore. Inside the document I have an array of 'friends'.
[Heres what my firebase backend looks like.
I'm pulling the whole document into my React JS project and saving it onto a global state called friendsList.
Inside my profile page component.The console logs the user attribute of each user in the friend array (so its definitely getting the data), console log output
Now inside the return section on my profile page component, I go to pass in the friend as an object and display the FriendItemUI component, however nothing displays and no error occurs. I even tried to just display a paragraph <p and log just the user ID of the currently selected array item, but nothing showed in the console log, no errors, and nothing displayed on the component itself.
Now if i place the
console.log(friend.friend.user)
inside the return statement, it will log the users ID to the console without any issues.
So it seems to be that i can log the information to the console, inside and outside the return function, but i cant pass the actual array object into another component and i cant actually visiually show any information from the array in a div?
I'm not sure what im doing wrong and any help is appreciated.
Profile Page Component:
render(){
const { friendsList } = this.props;
return (
<div>
<a class="waves-effect waves-light btn" onClick={() => this.sendFriendRequest()}>Add
Friend</a>
{ friendsList && friendsList.map(friendsList => {
{ friendsList.friends.map(friend => {
//friendsList is a firebase firestore document.
// friendsList.friends is the array 'friends' inside the document
(async() => {
console.log("waiting for variable");
while(typeof friend.friend.user == "undefined")
await new Promise(resolve => setTimeout(resolve, 1000));
(console.log("variable is defined: " + friend.friend.user))
return(
<div>
<p>{friend.friend.user}</p>
<FriendItemUI friend={friend}/>
</div>
)
})();
})}
})}
</div>
)
}
FriendItemUI.JS component
class FriendItemUI extends Component {
render() {
const { friend } = this.props;
// this log doesnt even display anything to the console
console.log(friend + 'test')
if(friend) {
return <p> testing {friend.friend.user} | {friend.friend.personalMessage} | {friend.friend.friendStatus} </p>
}
else {
return(
<div>
<p>error loading friendlist</p>
</div>
)
}
}}
export default FriendItemUI
Now heres where it gets even more weird, If i dont want to go through all the friends in the array object and just want to display one friend, I can with the following code. [this code will now pass the friends array object into the FriendItemUI component and successfully display the name, personalMessage and friendStatus which is all taken from the firestore document array object..
const { friendsList } = this.props;
{ friendsList && friendsList.map(friendsList => {
return (
<FriendItemUI friend={friendsList.friends[1]}/>
)
I'm just really confused, I dont want to hard code which index values of the array i want to show, I'm trying to make the component check to see how many objects in the array there is, loop through the array and pass each of the friends from the array into the FriendItemUI component.
I'm super silly!, turns out that console.log and manually rendering a state was fine because react JS was smart enough to only render once the state values were defined with my firebase data.
All i had to do was modify the code as per below and add a check to only loop through the array if the array state value is != null. If the array state value is == null then return a loading results text message.
everything now works. Posting this answer incase someone runs into a similar issue.
render(){
console.log(this.props); // debug log
const { friendsList, profile } = this.props;
console.log('test log' + profile);
if(profile.friends!=null){
return(
<div className="container">
<a class="waves-effect waves-light btn" onClick={() => this.sendFriendRequest()}>Submit Friend Request</a>
{ profile && profile.friends.map(profile => { //if valid survey, return
return (
<div>
<FriendItemUI friend={profile}/>
</div>
)
})}
</div>
)
} else{
return (
<div>
<p style={{textAlign: 'center'}}>Loading Results</p>
</div>
)
}
}
I have the following JSON Data coming out of Firebase RTDB:
{"\"1\"":{"itemDescription":"This is a description of the item.","itemName":"Item Name","itemValue":{"costAmount":200,"costType":"dol"}},
"\"2\"":{"itemDescription":"This is an item description.","itemName":"Item Name 2","itemValue":{"costAmount":500,"costType":"dol"}}}
and so on...
The data is parsed (json.parse) and stored a variable called parsedJSONData in state.
I've tried looping through it using other recommendations on this site and others, such as:
this.state.parsedJSONData.map(json => {
<div className="item">
<p>Item: {json.itemName}</p>
</div>;
});
And I'm getting errors like the below:
Line 68: Expected an assignment or function call and instead saw an expression no-unused-expressions
Any tips on what I can do? I know the data is parsed correctly, because if I output the contents of the parsedJsonData to the console, I can see the data is structured correctly?
Try Using forEach() instead of map()
You are not returning anything inside of your map(). Here you can look at the different ways to return values from an arrow function, but in short, data will be returned in these two forms:
json => json.itemName
json => {return json.itemName;}
Drop the {} inside of your map or throw a return inside like so:
this.state.parsedJSONData.map(({itemName}) =>
<div className="item">
<p>Item: {itemName}</p>
</div>
);
or
this.state.parsedJSONData.map(({itemName}) => {
return <div className="item">
<p>Item: {itemName}</p>
</div>;
});
Try this. I have tried this and it works for the your json Data.
render() {
var tempData = [];
const theDataWeHave = this.state.parsedJSONData;
for(var row in theDataWeHave){
var rowEntry = theDataWeHave[row];
tempData.push(rowEntry);
}
var renderVariable = tempData.map(function(item,index) {
return ( <RenderThisComponent key={index} item={item}/> )
})
}
return(
//your code for other stuff
{renderVariable} //Place this where you want to render the component
)
The component you want to render will be a separate functional component with props passed to it.
You can write in the same file or you can write in separate file and import+export
.I have done in the same file so it do not need to be exported or imported.
const RenderThisComponent = (props) => (
<div className="item">
<p>Item: {props.item.itemName}</p>
</div>
)
I am trying to use the sweetalert-react package (https://github.com/chentsulin/sweetalert-react) as a modal for my app.
Right now I got it to work, but I want to be able to display a const that has my component:
const clusterDoors = lock.doors.map(clusterDoor => {
return (
<div key={clusterDoor.port_id}>
<ClusterListItem
clusterDoor={clusterDoor}
customer={
clusterDoor.allocation.customer ? (
keyedCustomers[clusterDoor.allocation.customer]
) : (
false
)
}
.....
So I read their docs and found out that I can achieve that with ReactDOMServer.renderToStaticMarkup. So I simple need to:
text={renderToStaticMarkup(<MyComponent />)}
But the problem is that my component is inside of a constant, so if I try to do:
text={renderToStaticMarkup({clusterDoors})}
I will get the error:
You must pass a valid ReactElement.
I wonder if there's some workaround to this?
I did some research and tried also tried to do:
const clusterDoors = React.createClass({
render: function() {
lock.doors.map(clusterDoor => {
return (
<div key={clusterDoor.port_id}>
<ClusterListItem
clusterDoor={clusterDoor}
customer={
clusterDoor.allocation.customer ? (
keyedCustomers[clusterDoor.allocation.customer]
) : (
false
)
}
delivery={
clusterDoor.allocation.delivery ? (
keyedDeliveries[clusterDoor.allocation.delivery]
) : (
false
)
}
/>
</div>
)
})
}
})
But that didn't do the trick.
If I pass it a valid react component (ClusterListItem) my app won't break, but nothing will show because of the array clusterDoor wont exist.
I hope I explained my situation well. Thanks for reading.
The problem with your code is that you are passing an array of elements because clusterDoors is an array and renderToStaticMarkup is expecting a single element. Therefore you are getting this error.
Solution: Just wrap your array in a div tag so it becomes a single node element like this
text={renderToStaticMarkup(<div>{clusterDoors}</div>)}
So I just started trying to learn rxjs and decided that I would implement it on a UI that I'm currently working on with React (I have time to do so, so I went for it). However, I'm still having a hard time wrapping my head around how it actually works... Not only "basic" stuff like when to actually use a Subject and when to use an Observable, or when to just use React's local state instead, but also how to chain methods and so on. That's all too broad though, so here's the specific problem I have.
Say I have a UI where there's a list of filters (buttons) that are all clickeable. Any time I click on one of them I want to, first of all, make sure that the actions that follow will debounce (as to avoid making network requests too soon and too often), then I want to make sure that if it's clicked (active), it will get pushed into an array and if it gets clicked again, it will leave the array. Now, this array should ultimately include all of the buttons (filters) that are currently clicked or selected.
Then, when the debounce time is done, I want to be able to use that array and send it via Ajax to my server and do some stuff with it.
import React, { Component } from 'react';
import * as Rx from 'rx';
export default class CategoryFilter extends Component {
constructor(props) {
super(props);
this.state = {
arr: []
}
this.click = new Rx.Subject();
this.click
.debounce(1000)
// .do(x => this.setState({
// arr: this.state.arr.push(x)
// }))
.subscribe(
click => this.search(click),
e => console.log(`error ---> ${e}`),
() => console.log('completed')
);
}
search(id) {
console.log('search --> ', id);
// this.props.onSearch({ search });
}
clickHandler(e) {
this.click.onNext(e.target.dataset.id);
}
render() {
return (
<section>
<ul>
{this.props.categoriesChildren.map(category => {
return (
<li
key={category._id}
data-id={category._id}
onClick={this.clickHandler.bind(this)}
>
{category.nombre}
</li>
);
})}
</ul>
</section>
);
}
}
I could easily go about this without RxJS and just check the array myself and use a small debounce and what not, but I chose to go this way because I actually want to try to understand it and then be able to use it on bigger scenarios. However, I must admit I'm way lost about the best approach. There are so many methods and different things involved with this (both the pattern and the library) and I'm just kind of stuck here.
Anyways, any and all help (as well as general comments about how to improve this code) are welcome. Thanks in advance!
---------------------------------UPDATE---------------------------------
I have implemented a part of Mark's suggestion into my code, but this still presents two problems:
1- I'm still not sure as to how to filter the results so that the array will only hold IDs for the buttons that are clicked (and active). So, in other words, these would be the actions:
Click a button once -> have its ID go into array
Click same button again (it could be immediately after the first
click or at any other time) -> remove its ID from array.
This has to work in order to actually send the array with the correct filters via ajax. Now, I'm not even sure that this is a possible operation with RxJS, but one can dream... (Also, I'm willing to bet that it is).
2- Perhaps this is an even bigger issue: how can I actually maintain this array while I'm on this view. I'm guessing I could use React's local state for this, just don't know how to do it with RxJS. Because as it currently is, the buffer returns only the button/s that has/have been clicked before the debounce time is over, which means that it "creates" a new array each time. This is clearly not the right behavior. It should always point to an existing array and filter and work with it.
Here's the current code:
import React, { Component } from 'react';
import * as Rx from 'rx';
export default class CategoryFilter extends Component {
constructor(props) {
super(props);
this.state = {
arr: []
}
this.click = new Rx.Subject();
this.click
.buffer(this.click.debounce(2000))
.subscribe(
click => console.log('click', click),
e => console.log(`error ---> ${e}`),
() => console.log('completed')
);
}
search(id) {
console.log('search --> ', id);
// this.props.onSearch({ search });
}
clickHandler(e) {
this.click.onNext(e.target.dataset.id);
}
render() {
return (
<section>
<ul>
{this.props.categoriesChildren.map(category => {
return (
<li
key={category._id}
data-id={category._id}
onClick={this.clickHandler.bind(this)}
>
{category.nombre}
</li>
);
})}
</ul>
</section>
);
}
}
Thanks, all, again!
Make your filter items an Observable streams of click events using Rx.Observable.fromevent (see https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/events.md#converting-a-dom-event-to-a-rxjs-observable-sequence) - it understands a multi-element selector for the click handling.
You want to keep receiving click events until a debounce has been hit (user has enabled/disabled all filters she wants to use). You can use the Buffer operator for this with a closingSelector which needs to emit a value when to close the buffer and emit the buffered values.
But leaves the issue how to know the current actual state.
UPDATE
It seems to be far easier to use the .scan operator to create your filterState array and debounce these.
const sources = document.querySelectorAll('input[type=checkbox]');
const clicksStream = Rx.Observable.fromEvent(sources, 'click')
.map(evt => ({
name: evt.target.name,
enabled: evt.target.checked
}));
const filterStatesStream = clicksStream.scan((acc, curr) => {
acc[curr.name] = curr.enabled;
return acc
}, {})
.debounce(5 * 1000)
filterStatesStream.subscribe(currentFilterState => console.log('time to do something with the current filter state: ', currentFilterState);
(https://jsfiddle.net/crunchie84/n1x06016/6/)
Actually, your problem is about RxJS, not React itself. So it is easy. Suppose you have two function:
const removeTag = tagName =>
tags => {
const index = tags.indexOf(index)
if (index !== -1)
return tags
else
return tags.splice(index, 1, 0)
}
const addTag = tagName =>
tags => {
const index = tags.indexOf(index)
if (index !== -1)
return tags.push(tagName)
else
return tags
}
Then you can either using scan:
const modifyTags$ = new Subject()
modifyTags$.pipe(
scan((tags, action) => action(tags), [])
).subscribe(tags => sendRequest(tags))
modifyTags$.next(addTag('a'))
modifyTags$.next(addTag('b'))
modifyTags$.next(removeTag('a'))
Or having a separate object for tags:
const tags$ = new BehaviorSubject([])
const modifyTags$ = new Subject()
tags$.pipe(
switchMap(
tags => modifyTags$.pipe(
map(action => action(tags))
)
)
).subscribe(tags$)
tags$.subscribe(tags => sendRequest(tags))