Re-Rendering of ReactiveList Results - reactjs

I am trying to build a Search-Engine using reactivesearch library. Does anybody know how i can trigger a re-rendering of the ReactiveList Results? (e.g. with a button onClick event?)

If you have a class component, please try below code
this.forceUpdate();

Unfortunately i can not provide my full code here. But i will try to explain what my problem is. Within my main App component i do have ReactiveBase, a DataSearch and ReactiveList as well as several buttons:
const App = () => (
<ReactiveBase
app="Test"
credentials="null"
url="http://localhost:9200"
analytics={false}
searchStateHeader
>
<DataSearch />
<ReactiveList
componentId="result"
dataField="_score"
renderItem={renderItem}
>
<div><Switch defaultChecked onChange={onSpeciesChange} style={{ marginRight: '5px', background: "brown" }} id="cellines"/> <label> Celline </label></div>
<div><Switch defaultChecked onChange={onSpeciesChange} style={{ marginRight: '5px', background: "blue" }} id="chemicals"/> <label> Chemicals </label></div>
So the buttons get rendered within my main App component and i do have a function onSpeciesChange, which basically updates a global object called entityswitchstatus with boolean values:
function onSpeciesChange(checked,event) {
if (event.target.id === "cellines") { entityswitchstatus.cellines=checked; }
else if (event.target.id === "chemicals") { entityswitchstatus.chemicals=checked; }
else if (event.target.id === "diseases") { entityswitchstatus.diseases=checked; }
else if (event.target.id === "genes") { entityswitchstatus.genes=checked; }
else if (event.target.id === "mutations") { entityswitchstatus.mutations=checked;}
else if (event.target.id === "species") { entityswitchstatus.species=checked; }
console.log(entityswitchstatus);
}
Within the renderItem function of the ReactiveList component i am processing the responses from Elasticsearch. And if there is a certain field and the global entityswitchstatus is true i do a highlighting of another field of the elasticsearch response. That all happens within renderItem function of ReactiveList.
function renderItem(res) {
if (res.ptc_species && entityswitchstatus.species) { var species_classname = createHighlighClassObject(res.ptc_species,"species"); } else { res.ptc_species = [] }
}
And basically by clicking the buttons i can change the global object entityswitchstatus of course. But this does not lead to a re-rendering of the ReactiveList component which is also expected. I can not pass any additional props to renderItem or at least i don't know how. So my idea was to simply call re-rendering of ReactiveList component by also clicking the button within the main App component.
Hope this is not too confusing.

Related

React scroll to element (ref current is null) problem

I have a problem I'm not able to solve. The app got a component where a do looping array and making multiple elements off it. Then I want to make buttons in another component that will scroll to a specific element. (something similar to liveuamap.com when you click on a circle).
I tried the below solution, but got "Uncaught TypeError: props.refs is undefined". I could not find any solution to fix it.
The second question: is there a better or different solution to make scrolling work?
In app component I creating refs and function for scrolling:
const refs = DUMMY_DATA.reduce((acc, value) => {
acc[value.id] = React.createRef();
return acc;
}, {});
const handleClick = (id) => {
console.log(refs);
refs[id].current.scrollIntoView({
behavior: "smooth",
block: "start",
});
};
The refs I send to the article component as a prop where I render elements with generated refs from the app component.
{props.data.map((article) => (
<ContentArticlesCard
key={article.id}
ref={props.refs[article.id]}
data={article}
onActiveArticle={props.onActiveArticle}
activeArticle={props.activeArticle}
/>
))}
The function is sent to another component as a prop where I create buttons from the same data with added function to scroll to a specific item in the article component.
{props.data.map((marker) => (
<Marker
position={[marker.location.lat, marker.location.lng]}
icon={
props.activeArticle === marker.id ? iconCircleActive : iconCircle
}
key={marker.id}
eventHandlers={{
click: () => {
props.onActiveArticle(marker.id);
// props.handleClick(marker.id);
},
}}
></Marker>
))}
Thanks for the answers.
Ok so i found the solution in library react-scroll with easy scroll implementation.

Conditionally add function as prop in React?

I need to conditionally add a function in React (Native).
This code works but presumably the onScroll event is needlessly called when the Platform is web, which I assume is not ideal for performance?
<ScrollView
onScroll={() => {
if(Platform.OS === 'web') {
return;
}
container.current?.onScroll();
}}
>
Is there a way to conditionally set the onScroll prop?
You can but it's probably more effort than it is worth in terms of performance gains. There is very little cost in calling a function that does nothing.
You can build the props as an object outside of JSX and pass them in using the spread operator, like so:
const scrollViewProps = {}
if (Platform.OS !== "web") {
scrollViewProps.onScroll = () => {
container.current?.onScroll()
}
}
<ScrollView {...scrollViewProps} />
You can use && to create the condition:
<ScrollView
onScroll={Platform.OS !== 'web' && () => container.current?.onScroll()}
>

Toggling font awesome Icon using React

I want to toggle a fontAwesome icon class name on click. When clicked, the icon should change the color and also call a service which adds an object to a favorite list in the server (hence, why I use e.currentTarget: I need to remember which icon was clicked). This code works on the first click, but fails to change the class back on the second click (doing an inspect, it says the FA´s classname equals "Object object"). Any idea how I could fix it?
<FontAwesomeIcon onClick={this.ToggleClass} size={"sm"} icon={faHeart} />
ToggleClass = (e) => {
const heart = {
color: "#E4002B",
}
const clicked = {
color: "#E4002B",
background: "red"
}
if (e.currentTarget.className.baseVal != heart && e.currentTarget.className.baseVal != clicked) {
return e.currentTarget.className.baseVal === clicked;
#Callservicehere
}
else if (e.currentTarget.className.baseVal === clicked) {
e.currentTarget.className.baseVal = heart;
#callservicehere
}
}
You're not thinking in React yet :)
Accessing the event target and imperatively manipulating the DOM bypasses React's rendering - you might as well just be using jQuery. Not that there's anything bad about that, but it's not the right way to go about things in React.
In React, if you need to change the DOM in response to user interaction you do it in the render method, i.e. output different JSX based on the component's current state or props.
A couple things that might help here:
clicked and heart are both objects which means that you cannot compare them without using a deep comparison method.
var a = { id: 1 }
var b = { id: 1 }
console.log(a == b) //false
console.log(a === b) //false
If you want to compare them, you can convert them both to strings using the toString() method
heart.toString() === clicked.toString()
In your first if condition, it looks like you're returning a true/false value instead of assigning a desired classname to your target.
return e.currentTarget.className.baseVal === clicked // true/false
e.currentTarget.className.baseVal = clicked // assigned
You could also take the approach of keeping your classnames as strings and adding your styled objects inside of css
class MysteryComponent extends React.Component {
state = {
className: 'heart'
}
toggleClass = (e) => {
if (this.state.className === 'heart') {
this.setState({ className: 'clicked' })
} else if (this.state.className === 'clicked') {
this.setState({ className: 'heart' })
}
}
render() {
return (
<div className={this.state.className}>
<FontAwesomeIcon onClick={this.toggleClass} size={"sm"} icon={faHeart} />
</div>
)
}
}
// css
.heart {
color: "#E4002B";
}
.clicked {
color: "#E4002B";
background: "red";
}
I see. You want to fill/unfill the color of the heart as the user clicks. The reason why the results are not meeting your expectations is because of the event.targets are especially funky with FontAwesome. You may think you're clicking on it, but it manipulates the DOM in a way that when you try extract the className, it's value is often inconsistent.
This is why everyone is recommending that you make use of React's state. The logic that determines how elements are styled is now more controlled by the component itself instead of the FontAwesome library. Consider the code below, we only care about whether the item was clicked, not what class it initially has.
class Example extends React.Component{
state = {
clicked: false
}
handleOnCLick = () => {
this.setState({
clicked: !this.state.clicked
})
}
render(){
var clicked = this.state.clicked
return(
<button onClick={this.handleOnClick}>
<i
class={ clicked ? "fas fa-heart" : "fas fa-circle"}
</i>
</button>
)
}
}

React Native Dynamic Styling

I am creating s crypto app where I want to change background color to red when the price went down and green when Price went up and then after 2 seconds change the background back to normal using setTimeout.
I tried two different methods to at-least change the backgroundColor but on both the occasion i got the following error
You attempted to set the key backgroundColor with the value #ffe5e5
on an object that is meant to be immutable and has been frozen.
I asked the separate question for the same but for some reason, the response I received was not convincing.
Afterwords, i tried a different approach (the one which does not allow the use of StyleSheet) but I still got the same error.
I am putting my new code here (you can refer to my previous code from the question)
First I declared an object in a global scope like this
var upperRow = {
display: "flex",
flexDirection: "row",
marginBottom: 5,
backgroundColor: "white"
}
class CoinCard extends Component {
then I tried to change background color like this
componentWillReceiveProps(nextProps) {
if (this.props.coinPrice != nextProps.coinPrice ) {
if (this.props.coinPrice > nextProps.coinPrice) {
upperRow["backgroundColor"] = "#ffe5e5";
}
}
followed by assigning the styles like this
return (
<View style={container}>
<View style={upperRow}>
[Question:] How can I change the styling dynamically?
You'll need to keep in state whether or not your coin has decreased recently.
Imagine a state of this shape:
state = {
hasIncreased: boolean,
hasDecreased: boolean,
}
Now you can change this state in the componentWillReceiveProps (this is now deprecated though, so if you are on 16.3 or higher, maybe look in phasing its use out) like so:
componentWillReceiveProps(nextProps) {
if (nextProps.coinPrice < this.props.coinPrice)
this.setState({hasDecreased: true, hasIncreased: false})
if (nextProps.coinPrice > this.props.coinPrice)
this.setState({hasIncreased: true, hasDecreased: false})
}
Now that you have this state, you can add some styles conditionally:
render() {
const {hasIncreased, hasDecreased} = this.state
return (
<View style={[ //styles can be an array!
upperRow,
hasIncreased && {backgroundColor: 'green'},
hasDecreased && {backgroundColor: 'red'},
]}/>
)
}
Now all that's left is to reset state after 2 seconds. To me, it seems best to use the componentDidUpdate-lifecycle for that
componentDidUpdate(prevProps) {
// only queue the reset if it's actually necessary, not on random updates
if (prevProps.coinPrice !== this.props.coinPrice) {
clearTimeout(this.timeout)
// we need to be able to reset the timeout
this.timeout = setTimeout(
() => this.setState({hasDecreased: false, hasIncreased: false}),
2000,
)
}
}
You should try avoiding just changing the styles like that in a mutable manner. The whole point is that your state should reflect what you display.
Pass modalHeight dynamically from parent component as props
modalView is default style
<View style={[styles.modalView, { height: `${modalHeight}` }]}>

How to change react element (li) with onClick to be strikethrough?

I've been trying to set element to become strikethrough when I click on it, but unfortunately I couldn't, nothing happens.
var UserList = React.createClass({
getInitialState: function() {
return {
user: [],
firstName: '',
lastName: '',
createdAt: 0,
isClicked: false,
};
},
handleOnClick: function() {
var isClicked = this.state.isClicked;
var style = {textDecoration: 'none'};
if (isClicked === true) {
style = {textDecoration: 'line-through'}
}
},
render: function() {
return (
<div>
<Users user={this.state.user} onClick={this.handleOnClick}/>
</div>
);
You can do it this way:
A similar example to yours:
const TodoItem = ({item, checkHandler}) => {
const itemCheckHandler = () => {
checkHandler (item.id);
};
return (
<div>
<li
style={{
textDecoration: item.checked ? 'line-through' : 'none',
}}
onClick={itemCheckHandler}
>
{item.text}
</li>
</div>
);
};
and your checkHandler in your App.js where the state resides is like this (items bein an array of items):
checkHandler = id => {
this.setState ({
items: this.state.items.map (item => {
if (item.id === id) {
item.checked = !item.checked;
}
return item;
}),
});
};
Don't try to change the style in a click handler. You should not change the style when a user does an action but rather do it at the time of it being rendered, that's the correct approach.
Store the "strikethrough" value in a flag in the state and do it in the render function.
For example:
getInitialState: function () {
return {
...
isStrikeThrough: false,
...
}
},
onHandleClick: function () {
....
// toggle the strikethrough state
this.setState({isStrikeThrough: !this.state.isStrikeThrough});
....
},
render: function () {
return (
<div>
<User
user={this.state.user}
strikeThrough={this.state.isStrikeThrough}
onClick={this.handleOnClick}
/>
</div>
);
},
You haven't given any details about the User component, so the explanation above is based solely on what we have in the question. That said, there are a couple of ways in which this could be improved.
First, I'm assuming that you can add the strikethrough flag to the User component and render the <strike>...</strike> (or comparable CSS styles) there. That may or may not be true (ie. if the User component is a third-party component, it may be difficult to change it).
Second, the strikethrough state described above looks to me like it ought to be internal to the User component. If all you're doing is changing the markup in the User component based on a click on the User component, then the strikethrough code ought to be in the User component. And, perhaps more importantly, if the strikethrough is supposed to represent something important about the state of a user, something that should be saved as part of the user's state, then the strikethrough flag ought to be part of the user's state (and have a more informative name than isStrikeThrough).
Dodek you can see the above answers but I think you need to change the way you look and think about a react application then it helps you a lot during your coding with react. The code you provided looks like a jQuery approach to me that you directly modify the DOM element when user do an action. Even if there was no issue in your approach still your code does not apply 'line-through' style to an already checked element which you get them from backend unless user clicks on an item.
You should look at your component as a very simple actor in a movie that accepts a very small set of parameters (compared to real word) and based on this input parameters it changes the way it appears in the frame. For example lets say you have a Todo item component (Like the one #Vennessa has provided here) in a vey simple case it accepts only an item text and also whether or not the item is checked;
These parameters may come from internal state or come from props or any other resources but in the end your component is accepting these parameters and all your internal logic that determines how your component should look must only rely and work with these params.
Try this out:
function Item(props){
const [clicked, setIsClicked] = useState(false);
function handleClick(){
setIsClicked((prevValue) => {
return (!prevValue)
});
}
return <li style={{textDecoration: clicked ? "line-through": "none" }} onClick={handleClick}>
{props.text} </li>
}

Resources