I'm new to reactjs. I'm good in javascript and jQuery, but dumb in ReactJS. I have this jQuery code and I need to make it work with reactjs. This function is supposed to auto-scroll the list vertically on a loop. But I don't have any idea how to do this in react.
function autoScroll(obj) {
$(obj).find("container").animate({
marginTop: "-28px"
}, 500, function () {
$(this).css({
marginTop: "0px"
}).find("li:first").appendTo(this);
});
}
$(function () {
setInterval('autoScroll(".container")', 3000);
})
Given my component
import React from 'react'
function List(props) {
const lists = props.list
const list_div = lists.map((lists, index) => {
return (
<li key={index}>{lists}</li>
)
})
return(
<ul className="container">{list_div}</ul>
)
}
export default List
Will appreciate any help.
Step 1: Add ref to your components
//Create ref for parent component
const containerRef = React.createRef()
//Set the created Ref to the element
<ul className="container" ref={containerRef}>{list_div}</ul>
Step 2: Create refs to child components
//Create ref to child components
lists.map((list,index) => listsRef[index] = React.createRef())
Step 3: In your event (either click, load, etc), add this code to automatically scroll in one of the child component
this.containerRef.current.scrollTo({
top: listsRef[index].offsetTop,
left: 0,
behavior:'smooth'
})
Related
I have a problem to reinitialize the ion-phaser function component in a parent component. It works fine by reinitilization just as a class component. Bellow are the two examples to displays which works and which not.
Here is my parent render function:
render() {
return(
<>
{this.state.visible && <IonComponent />}
</>
)
}
Here is the Ion-Phaser function component (this doesn't work):
let game = { ..here comes the Phaser game logic }
const IonComponent = () => {
const [initialize, setInitialize] = useState(false);
useEffect(() => {
if (game?.instance === undefined) {
setInitialize(true);
}
}, [initialize]);
return (
<>
{ initialize && <IonPhaser game={game} initialize={initialize} />}
</>
)
}
export default IonComponent;
Here is the Ion-Phaser class component (this works):
class IonComponent extends React.Component {
state = {
initialize: true,
game: { ..here comes the Phaser game logic }
}
render() {
const { initialize, game } = this.state
return (
<IonPhaser game={game} initialize={initialize} />
)
}
}
export default IonComponent;
When I switch in the parent component the state.visible at the first render to true, it initiate the child IonPhaser component without any problems. But after the state.visible switch once to false and then again back to true, the function component will not reinitialize and it removes the canvas from the dom. The class component however works fine.
Is this a persistent bug in Ion-Phaser by function component or am I doing something wrong?
Make sure you're keeping track of the state of your game using React's useState() hook or something similar.
For what it's worth, I also ran into some issues while trying to get the IonPhaser package working. To get around these issues, I've published an alternative way to integrate Phaser and React here that (I think) is more straightforward.
It works mostly the same way, but it doesn't come with as much overhead as the IonPhaser package.
npm i phaser-react-tools
And then you can import the GameComponent into your App.js file like so:
import { GameComponent } from 'phaser-react-tools'
export default function App() {
return (
<GameComponent
config={{
backgroundColor: '000000',
height: 300,
width: 400,
scene: {
preload: function () {
console.log('preload')
},
create: function () {
console.log('create')
}
}
}}
>
{/* YOUR GAME UI GOES HERE */}
</GameComponent>
)
}
It also comes with hooks that let you emit and subscribe to game events directly from your React components. Hope this helps, and please get in touch if you have feedback.
I am adding Cards dynamically in my React functional component. Cards are stored in State. I map them and give id to each of them. OnClick on those Cards I get their id successfully. Now I want to getElementById to change Card color:
function Clicked(pressedGifId) {
if (pressedGifId === 'correctGif') CorrectMatch();
else WrongMatch();
}
function CorrectMatch(pressedGifId) {
// / THERE I GET Element: null
console.log('Element:', document.getElementById(pressedGifId));
}
function WrongMatch() {
console.log('wrong a match!');
}
export default function GameObject(props) {
const addedToGameGif = [];
const [pressedGifId, gifPressed] = useState(null);
const [photoCards, setPhotoCards] = useState([]);
useEffect(() => {
Clicked(pressedGifId);
}, [pressedGifId]);
// add randomly picked photos to addedToGameGif array
// ...
addedToGameGif.map(gifId =>
photoCards.push(
<Card id={gifId} onClick={() => gifPressed(gifId)}>
text
</Card>,
),
);
return <div>{photoCards}</div>;
}
I tried learning refs but they are only for class components. So how do I reach my element by id in React?
You can use ref in functional component as well. There is a hook called useRef.
Note: Never interact directly with DOM until or unless there is no api available in react to solve the problem for that particular use case.
In react it's not recommended to interact directly with dom. Always use react apis to interact with dom. React is designed to hide the DOM because they want to abstract the DOM away. By using the DOM directly you break the abstraction and make your code brittle to changes introduced in the library.
React is maintaining a virtual DOM if we make any changes in actual DOM directly then react will not be aware of this change and this can lead to some unexpected behavior .
import React, {useState, useRef} from 'react';
export default function GameObject(props) {
const addedToGameGif = [];
const [pressedGifId, gifPressed] = useState(null);
const [photoCards, setPhotoCards] = useState([]);
const elemRef = useRef(null);
useEffect(() => {
Clicked(pressedGifId);
}, [pressedGifId]);
// add randomly picked photos to addedToGameGif array
// ...
addedToGameGif.map(gifId =>
photoCards.push(
<Card ref={elemRef} id={gifId} onClick={() => gifPressed(gifId)}>
text
</Card>
)
);
return <div>{photoCards}</div>;
}
Example from official docs.
function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
I have a container populated dynamically with elements based on an array in the Redux store. A code example follows.
const state = {
elements: [
{
id: "element_1",
attrs: {
width: 200,
height: 100,
backgroundColor: "#ff0000"
}
},
{
id: "element_2",
attrs: {
width: 50,
height: 300,
backgroundColor: "#00ffff"
}
}
]
};
// Elements Container (elements coming from Redux)
const ElementsContainer = ({ elements, elementsRefs }) => (
<>
{elements.map(element => (
<div
key={element.id}
ref={el => {
elementsRefs.current[element.id] = el;
}}
/>
))}
</>
);
// Parent container (elements coming from Redux)
const ParentContainer = ({ elements }) => {
const elementsRefs = React.useRef({});
React.useEffect(() => {
// ...some code to animate the elements through the refs
}, [elements]);
return (
<>
<ElementsContainer elementsRefs={elementsRefs} />
</>
);
};
The issue is that Redux dispatches the updated state first to the parent, then to the child (ElementsContainer). Whenever I add an element to the state, the refs are assigned only after useEffect (which is meant to trigger some DOM animations with the GSAP library leveraging the ref) has been executed, meaning that useEffect cannot find the ref yet.
So far this is how I solved:
I added an early return inside useEffect in case the ref doesn't exist
I created a state in the parent that keeps count of the elements manipulation
I pass down the setter of the state to the element, triggering it inside the ref callback
I added the state that keeps count as a dependency of useEffect
All of this causes two renders for each manipulation of the elements list. I really dislike this solution and I wonder whether you can suggest a better pattern.
I made a PoC of the app using Context instead of Redux and it works as intended (here you can find the fiddle), but I still would like to hear suggestions.
I am using material UI and react-sticky and its working good but i got one issue.
https://codesandbox.io/s/xv41xzvyp
I have shared what i have tried yet.
Step to reproduce
Go to bottom of first tab and tab will stick
Try to go another tab but content will stay there it need to scroll to start position and
The StickyContainer component has a property node that is a ref to the topmost element of the container, so you can scroll that into view with the help of a ref of your own:
class CustomizedTabs extends React.Component {
ref = React.createRef();
state = {
value: 0
};
handleChange = (event, value) => {
this.setState({ value }, () => this.ref.current.node.scrollIntoView());
};
render() {
const { classes } = this.props;
const { value } = this.state;
return (
<div className={classes.root}>
<StickyContainer ref={this.ref}>{/* ... */}</StickyContainer>
</div>
);
}
}
I am currently working on a react app,
and I need to implement changes on the screen that occurs when an element is on a certain position on the screen (on certain offsetTopValue),
this should be done dynamically because every element has a unique id that is created dynamically.
(it also contains 3 purecompenets and 2 functional components down the hierarchy
and most props are passed down using context)
index.jsx
class Collection extends Component {
<CollectionContext.Provider
value={{state: this.state, cards: this.props.cards...}}>
.......
ideal: (
<CollectionLayout cards= {this.props.cards ..../>
.......
</CollectionContext.Provider>
}
layout.jsx
componentDidMount () {
this._bindScroll();
}
_handleScroll = () => {
var html = document.documentElement;
this.updateCardsPositions();
}
render(
...
return(
<CollectionContext.Consumer>
{context =>
<Loader
randomSpin= {this.props.randomSpin}
currentTopic = {this.props.currentTopic}
/>}
<CollectionContext.Consumer>
);
)
loader.jsx
export const Loader = ({ randomSpin, currentTopic}) => (
.....
{!context.state.spinning &&
context.cards.map((card, index) => <Card key={index} card=
{card}/>)}
cards.jsx
export const Card = ({ card } **id={card.id}**) => {
return (...);
}
what I do have: I have a unique card id that is passed to each card,
and I can access the cards id in the layout.
I tried using refs but am not yet sure about the proper way
to pass it through all those components and access it in ComponentDidMount()
in the layout.
I tried utilizing the context but it can be accessed only inside render and not on componentDidMount.
I am also not sure how should I use the refs callback:
({(el)=>{inputRef = el}}) with a dynamic value (every card has a unique id that should be refed.
any other suggestions on how to get the offset top values from the layout would help.
I will appreciate any help or a general solution direction with it.
Thank You!