How can I define state based on another state property? - reactjs

I come from the Ember world so apologies if this question is very basic (I'm sure it is). I have a component which sets the state "scrollPosition" whenever the window is scrolled. I would like to define a new state property, "isScrolledToTop", which is equal to true when "scrollPosition" is 0.
In Ember I would have simple defined a new property and checked the condition when scrollPosition changed. Not quite sure how to do this in React. I was thinking of using "componentDidUpdate", but pretty sure this is not the right approach. Thanks for the help in advance!

If you're asking "how can I fire some event once the user scrolls to the top of the page" then the answer is to do that in the onscroll event callback.
If you want to render something differently (e.g. a full-size header) when the scroll position is at the top of the page, then the logic would go in the render() method of your component. Something like:
render() {
if (this.state.scrollPosition === 0) {
// do something specific to this scenario
}
return ...
}
This part of the docs suggests not putting 'computed data' in state, which is what isScrolledToTop is.
As an FYI, if you're tracking the scroll position, the window object keeps this in its 'state' for you, available at window.scrollY - you don't need to duplicate this into your component's state.

Related

React don't mount component until needed but don't unmount afterwards in list of components

Say I am building an instant messaging with app with React (I'm not doing that exactly, but this is easier to explain). I have a sidebar with a list of conversations and, when you click one, it is shown on the right (similar to this). I don't want to mount each conversation component until the user clicks it, but I don't want to unmount it, just hide it, when they click on another conversation. How can I do this cleanly? There will never be more than about 30 chats for any user.
You can store the enabled conversations in an array that you use to show, and when you disable a conversation you can just add a hidden prop to it which you pass to the conversation and make it return null. This will make it not render anything but will not unmount it since you have not removed it from the array that handles the display of conversations.
example at: https://codesandbox.io/s/wispy-forest-59bqj
This is a bit hard to answer since you haven't posted the code.
But, theoretically, the best way to approach this problem is to transfer the data from your sidebar component and load it onto the right component on a per-user basis. You don't have to mount each "conversation component".
You can do this by with the boolean hidden property in your markup. React will render as usual and simply pass it along to the html, the browser will then simply not paint it.
const HideMe = ({ isHidden }) => (
<div hidden={isHidden}>
can you see me?
</div>
)
I made an example for you:
https://codesandbox.io/s/elastic-curie-t4ill?file=/src/App.js
reference: https://www.w3schools.com/tags/att_hidden.asp

How to get height of an element at each page update

I have a content area on my page that displays data that gets to be pulled through from an API. I hooked it up so once data gets pulled from the API, it gets saved to the Redux store and page displays it. Now, I need to add a scroll to top and bottom buttons so if page is longer than given amount lets say 600px, they will appear. Odd thing is that, at first load of the page, API query does not kick in therefore page is empty so buttons should not appear.
Given all this, I don't see a way but to do this within componentDidUpdate()
componentDidUpdate() {
let ContentArea = document.getElementById('contentArea');
if(ContentArea != null & ContentArea.clientHeight > 0){
this.props.pageContentHeight(ContentArea.clientHeight)
}
}
and save the height value to Redux store so in the code I can display the scrolls like this:
{(contentHeight > 600) && <Scroll to="bottom" /> }
<Router page={pageURL} />
{(contentHeight > 600) && <Scroll to="top" /> }
contentHeight above is coming from redux store.
From what I read, it's not recommended to dispatch within React update lifecycle especially since dispatch causes update lifecycle to kick in too, it might cause a endless cycle. Is there any way I can accomplish that you guys think of adding scroll to top or bottom buttons onto page?
There's this package allows scrolling bottom but not top:
https://www.npmjs.com/package/react-scroll-to-bottom
It is perfectly okay to dispatching actions from life cycles. Just make sure to add guards to avoid infinite loops. That is, only dispatch an action from componentDidUpdate if the data needs to be updated.
Thank you for #pgsandstrom for his help on the topic.
For anyone trying to implement Scrollbar for content area, I've actually set up Redux to work using componentDidUpdate but then, found out even better approach. What if we can check body height and window inner height and compare then decide based on that? so below code exactly does that. Essentially returns true if scrollbar is visible on the page and false if not, then you can decide to display Scrollbar based on that.
document.body.scrollHeight > document.body.clientHeight
I did below:
componentDidUpdate() {
if(document.body.clientHeight > window.innerHeight) {
document.getElementById("topPage").style.display = "block";
document.getElementById("bottomPage").style.display = "block";
} else {
document.getElementById("topPage").style.display = "none";
document.getElementById("bottomPage").style.display = "none";
}
}
make sure to place it inside a component that can recognize the update.

Why "selected" not rendered here?

I am trying to understand how React works with the new Hooks implementation. In this example, I want the browsers to render selected items as I click on the rendered options. But as you can see, it doesn't work.
Here is the example: https://codesandbox.io/s/pjorxzyrx7
Do I have to use the useEffect in this case? Also, as I understand, useEffect couldn't render anything and only return functions. So, what am I missing here?
Thank you!
You're currently mutating the contents of the selected array instead of replacing it. React can't detect a state change when you do this.
Try something like the following:
const handleSelected = item => {
console.log(item);
console.log(selected);
setSelected([...selected, item]);
};
When updating arrays or objects as a part of a state, always make a new copy to assign so that React can properly know when to re-render.
Also, include relevant parts of your code directly in the question in the future, instead of hiding it behind a link (although including a runnable example is great!)

Store update doesnot reflect

I am following React & Redux. I have actions, reducers working separately. I am managing state more or less properly.
I am trying to create a ui which is having few textboxes and a button(disabled). In text boxes i had added onchange event on which it call a function
onChangeTextbox(){
}
In this function I do the following thing.
1. I update the state of the redux store.
onChangeTextbox(){
updateStateOfTextBox();
}
2.After doing this I look for whether all the text boxes, in the ui, is having something in it. If so I will enable my button to do further operations.
onChangeTextbox(){
updateStateOfTextBox();
updateStateOfButton();
}
Everything is working good except the one thing.
That one thing is as soon as I give the last empty textbox one character, the button is not enabled immediately, and when I give more character the button gets enabled. Similarly vice versa for disabling button.
The problem which I found is that when control complete its job of function updateStateOfTextBox(); and enters the updateStateOfButton(); function the state remains same. And again when render() occurs the change in state is reflected then.
I want to fix that issue and I am not getting any way out. Any solution and suggestion to this will be appreciated.
Thanks.
I found the solution to such situation.
componentsWillRecieveProps(newProps){
}
this method of the life cycle changes the game.
componentsWillRecieveProps(newProps){
newProps.state // this will give you new updated state which your action updated.
this.props.state // this will give you local or your state
// Here you can update your local state with global state
// And call the other function here as
updateStateOfButton();
}
Therefore the solution can be interpreted like
onChangeTextbox(){
updateStateOfTextBox();
}
componentsWillRecieveProps(newProps){
.
.
updateStateOfButton();
}

ReactJS: Easily access state objects from data-reactid attributes?

I'm working on my first react app. I noticed that the state that contributes to an element is occasionally reflected in the reactid. Maybe it's the key that was passed?
I can't find a lot of documentation on reactid but I was wondering if there was a good way to isolate those keys.
for example, an element that I'd like to update has the ID: .0.1.0.1.$4.3:$level.1
The $ represent known indices of the state object I used to build that DOM node. (specifically, this is the object this.state.figures[4].level)
It would be really awesome if I could parse those $ values out with a predefined method, to make it easy to setState. Does such a thing exist?
This is me trying to setState on events defined by the top ancestor to avoid cumbersome bidirectional event handling. Am I being really foolish with my approach?
if I understand correctly you're looking to bind events to DOM nodes based on their data-reactid's?
If so - I don't think that's a wise idea at all. The data-reactid property is qutie transient, it changes without warning and is influenced by a number of events in the React ecosystem.
I like to think of DOM generated by React as a compile target, something I'm not really even supposed to interact with at all. A black box, if you will.
If you need help solving an eventing issue, you may want to describe that itself.
EDIT
You can add event handlers in React which are aware of their position in an iterator, and aware of the current state of the component.
<div>
{
things.map((thing, index) => {
return (
<li
className="level"
key={`level_${index}`}
onClick={this.handleClick.bind(this, thing)}>
{ thing }
</li>
);
});
}
</div>
So handleClick would receive the thing item when clicked, which could give you information about that particular thing. You could pass the index too. In handleClick you'll also have access to this.state.

Resources