I'm trying to make a div that gives a count of overflowed items something like this:
[item1] [item2] [+4]
where the +4 is the amount of items that couldn't fit into the div.
I've able to do this based on count but doing it based on width is tough.
Are there any ways to at render time see if a component is going to overflow it's parent div?
MY code so far is below.
import React, { Component } from "react";
import { Button } from "antd";
import ReactDOM from "react-dom";
class ReciepesList extends Component {
state = { elWidth: "" };
componentDidMount() {
//determineWidth();
}
determineWidth() {
var inwidth = ReactDOM.findDOMNode(this.refs.inner).getBoundingClientRect()
.width;
var inheight = ReactDOM.findDOMNode(this.refs.inner).getBoundingClientRect()
.height;
var outwidth = ReactDOM.findDOMNode(this.refs.outer).getBoundingClientRect()
.width;
var outheight = ReactDOM.findDOMNode(
this.refs.outer
).getBoundingClientRect().height;
console(
"inW",
inwidth,
"outW",
outwidth,
"inH",
inheight,
"outH",
outheight
);
}
render() {
const { reciepes, all, style } = this.props;
let count = 0;
return (
<div ref="outer">
<div ref="inner" style={{ overflowY: "hidden" }}>
{reciepes.map(reciepe => {
//NEED SOLUTION HERE TO CHECK WIDTH
if (count < 6 || all) {
count++;
return (
<Button type="dashed" key={reciepe}>
{reciepe}
</Button>
);
} else {
return null;
}
})}
{reciepes.length - count > 0 && (
<Button type="dashed">+{reciepes.length - count}</Button>
)}
</div>
</div>
);
}
}
export default ReciepesList;
Related
I am trying to code a chessboard in order to prepare for an interview from this - challenge
I want it to look like this -
When you click on any tile of the chessboard all the diagonals change color to red.
Till I have achieved to display the chessboard using 2d for loop and tile component.
This is how I have done it -
app.js
import Chessboard from './Chessboard'
function App() {
return (
<div className="app">
<Chessboard/>
</div>
);
}
export default App;
chessboard.js
import React from "react";
import '../Chessboard.css';
import Pixel from "./Pixel";
const horizontalAxis = [ "a", "b","c","d","e","f","g","h"]
const verticalAxis = [ "1", "2","3", "4","5","6","7","8"]
function Chessboard() {
let board=[]
for (let j = verticalAxis.length - 1; j >= 0; j--) {
for (let i = 0; i < horizontalAxis.length; i++) {
if((i+j)%2===0)
{
board.push(<Pixel key={i.toString()+j.toString()} selectedColor="black"/>)
}
else
{
board.push(<Pixel key={i.toString()+j.toString()} selectedColor="white"/>) // board.push(<div className="tile white-tile"></div>)
}
}
};
return (
<div id="chessboard">
{board}
</div>
);
}
export default Chessboard;
Pixel.js
export default function Pixel(props){
const {selectedColor} = props
return (<div className="tile" style={{ backgroundColor: selectedColor }}></div>)
}
pixel css
.tile{
width: 100px;
height: 100px;
}
This looks like this
Now this where I am stuck, how do I make the tiles clickable?
I tried onClick function and using events like this
function test(_e){
console.log("works")
}
for (let j = verticalAxis.length - 1; j >= 0; j--) {
for (let i = 0; i < horizontalAxis.length; i++) {
if((i+j)%2===0)
{
board.push(<Pixel onClick={e => test(e)} key={i.toString()+j.toString()} selectedColor="black"/>)
}
else
{
board.push(<Pixel key={i.toString()+j.toString()} selectedColor="white"/>) // board.push(<div className="tile white-tile"></div>)
}
}
};
this should console log message when you click on black tiles but this does not work.
I am stuck as to I have no idea on how to display the diagonal red colour on clicking of a tile.
What can I do to solve this?
Edit: Made changes to pixel.js now clicking of div works and colour changes
export default function Pixel(props){
const {selectedColor} = props
const [pixelColor, setPixelColor] = useState(selectedColor);
const [originalColor, changeOriginalColor] = useState(true);
function applyColor() {
if(originalColor)
{
setPixelColor("#FF0000");
changeOriginalColor(false);
}
else{
setPixelColor(selectedColor);
changeOriginalColor(true);
}
}
return (<div
className="tile"
onClick={applyColor}
style={{ backgroundColor: pixelColor }}
></div>)
}
Now I cant figure out how to do the diagonal thing where if you click on any one tile all the diagonals will turn red as shown in the picture above.
How should I trigger it from chessboard.js?
Any solution for that is appreciated, I am stuck over here.
This is already explained in this question: onClick does not work for custom component
That said, in your code, the onClick prop is unused in your Pixel component right now, so passing it down to the DOM element would be enough to unblock you.
export default function Pixel({ selectedColor, onClick }) {
return (
<div
onClick={onClick} // <--- Here
className="tile"
style={{ backgroundColor: selectedColor }}
/>
);
}
Here is a program. By pushing Add Button you can add a button. Pushing the Show current number of buttons you can show current number of buttons.
But it appears every button has its own idea about how many buttons we currently have. Every button somehow “remembers” the number_of_buttons we had when it was “born”, and does not see the current value of this state.
I want state property number_of_buttons to be the same for every element in the hook. How to do this?
import React, { useState, useEffect } from 'react';
import ReactDOM from "react-dom";
var just_key = 50;
class App extends React.Component {
render() {
return (
<SomeCrazyButtons/>
);
}
}
function SomeCrazyButtons() {
const [number_of_buttons, setButtonsNumber] = useState(1);
const [some_html, setSomeHtml] = useState([]);
function addClickNumber() {
setButtonsNumber(number_of_buttons + 1);
}
function showCurrentNumberOfButtons() {
console.log("Current number of buttons is " + number_of_buttons);
}
useEffect(() => {
just_key++;
console.log("We have " + number_of_buttons + " buttons");
var new_button = [
<button key = {just_key + "b"} onClick = {showCurrentNumberOfButtons}>Show current number of buttons</button>
];
var new_buttons = some_html.concat(new_button);
setSomeHtml(new_buttons);
}, [number_of_buttons]);
return (
<div>
{some_html}
<button onClick = {addClickNumber}>Add button</button>
</div>
)
}
ReactDOM.render(
<App />,
document.getElementById('root')
);
Don't store components in state. As you've discovered, it's an easy way to end up rendering stale components, because you forgot to update your stored components to match your new state. Instead, just store the minimal data needed to define the state (in this case, the number of buttons), and create the components when you render.
function SomeCrazyButtons() {
const [number_of_buttons, setButtonsNumber] = useState(1);
function addClickNumber() {
setButtonsNumber(number_of_buttons + 1);
}
function showCurrentNumberOfButtons() {
console.log("Current number of buttons is " + number_of_buttons);
}
const buttons = [];
for (let i = 0; i < number_of_buttons; i++) {
buttons.push((
<button key={i + 'b'} onClick={showCurrentNumberOfButtons}>
Show current number of buttons
</button>
);
}
return (
<div>
{buttons}
<button onClick={addClickNumber}>Add button</button>
</div>
);
}
I have a react component that allows for a resizable text area based on the text inside.
return (<div className={`resizable-textbox ${className}`}>
<textarea
value={value}
onChange={onChangeMade}
onBlur={onBlur}
readOnly={readOnly} />
</div>);
The onChangeMade method looks like this:
const onChangeMade = (e) => {
const scrollHeightPadded = e.target.scrollHeight;
if ((scrollHeightPadded + "px") !== e.target.style.height) {
e.target.style.height = 0;
const height = Math.max(scrollHeightPadded, 31) + 3;
e.target.style.height = `${height}px`;
}
}
This is a slightly ugly method I know, needs cleaning up. However I want to call this method once on the first load of the component but e is an event triggered by the textarea tag.
Is there a way to hook into this or directly into the component with the method? (I am using React Hooks and stateless components).
Thanks.
You can use createRef to create a ref and give it to the ref prop of your textarea and use that instead of the event.
Example
class MyComponent extends React.Component {
ref = React.createRef();
componentDidMount() {
this.onChangeMade();
}
onChangeMade = () => {
const { current } = this.ref;
const scrollHeightPadded = current.scrollHeight;
if (scrollHeightPadded + "px" !== current.style.height) {
current.style.height = 0;
const height = Math.max(scrollHeightPadded, 31) + 3;
current.style.height = `${height}px`;
}
};
render() {
return (
<div className={`resizable-textbox ${className}`}>
<textarea
ref={this.ref}
value={value}
onChange={onChangeMade}
onBlur={onBlur}
readOnly={readOnly}
/>
</div>
);
}
}
I want my code to toggle a person handler, Before it was working but since I split into components, It seem to have broken.
Toggle happens on button click (see inside return statement <
button className={btnClass}
onClick={props.toggler}>Button</button>
Here is my entire cockpit.js file (inside src/components/cockpit/cockpit.js).
import React from 'react';
import classes from './cockpit.css';
const Ccockpit = (props) => {
const assignedClasses = [];
let btnClass = ''
if (props.cocPersonState) {
btnClass = classes.red;
console.log(".......")
}
if (props.cocperson <= 2) {
assignedClasses.push(classes.red)
}
if (props.cocperson <= 1) {
assignedClasses.push(classes.bold)
}
return(
<div className={classes.cockpit}>
<h1> Hi I am react App</h1>
<p className={assignedClasses.join(' ')}>hey </p>
<button className={btnClass}
onClick={props.toggler}>Button</button>
</div>
);
}
export default Ccockpit;
and inside App.js
return (
<div className={classes.App}>
<Ccockpit>
cocPersonState = {this.state.showPerson}
cocperson = {this.state.person.length}
toggler = {this.togglerPersonHandler}
</Ccockpit>
{person}
</div>
)
}
}
and this is my togglerpersonHandler code.
togglerPersonHandler = () => {
const doesShow = this.state.showPerson;
this.setState({
showPerson: !doesShow
});
}
I can't see to figure out that why it won't toggle and console.log/change color to red (It isn't changing the state). Can someone please review and figure out the mistake?
Your JSX still isn't right. Please review the JSX syntax with regards to giving it props/children.
You have this:
<Ccockpit>
cocPersonState = {this.state.showPerson}
cocperson = {this.state.person.length}
toggler = {this.togglerPersonHandler}
</Ccockpit>
But those values aren't children, they're properties. So they need to be in the opening tag, like this:
<Ccockpit
cocPersonState = {this.state.showPerson}
cocperson = {this.state.person.length}
toggler = {this.togglerPersonHandler}/>
Revisit some React tutorials to see how JSX should be structured and how it works.
I've made this little game in React.js:
Demo: https://door-game.netlify.com/
App.js file: https://github.com/Blazej6/Door-game/blob/master/src/App.js
I want to render a picture in the center button that matches the choosen framework. 3 Vue renders vue, 3 react - react etc.
How do I make the logic to do that?
Did some experimental approches, like placing a color class anchor inside app and circle components but it seems to not reading current state at all, at least not from current angle, also tried to actualy use react router and encolse circle component in a link, but that really screws up the css for whatever reason
Is there really no one up to the task?
For a simple app like this, there is no need to integrate redux/mobx yet. What I recommend is something that is very common in React, and that is to lift your state up.
We can accomplish this through three steps:
Dumb down the Circleone, Circletwo, Circlethree components. They only need to know what the current angle is in order to render
ClawCircle should be told what image to render (or otherwise blank)
App needs to hold the state for all this information (and thus we've "lifted" the state up from CircleX to its parent, App).
Step 1
Instead of holding the currentAngle in the state, let's assume that information is given to us through the prop currentAngle. When a circle gets clicked, we'll just tell whoever created the circle that we were clicked on, because they will pass us a prop called onClick.
Since we now don't need to keep track of our state, we can make the component stateless and just turn it into a functional component.
For example, CircleOne might turn out to look more like this:
const CircleOne = ({ currentAngle, onClick }) => (
<div
className="App-logo small-logo"
alt="logo"
style={{ transform: `rotateZ(${currentAngle}deg)` }}
onClick={onClick}
>
<div className="little-circle one react">
{/* ... rest of your divs */}
</div>
);
Step 2
Next, let's change ClawCircle, we'll give it an optional imageClass prop that might be claw-react, claw-vue etc, or it might just be an empty string (update css accordingly to render the image too!). So the render method might change into this:
render() {
const circleStyle = { transform: `rotateZ(${this.props.currentAngle}deg)` };
return (
<div
className={`App-logo claw-circle ${this.props.imageClass}`}
alt="logo"
style={circleStyle}
onClick={this.rotateCircle.bind(this)}
/>
);
}
By the way, the bind call can be done in the constructor instead of the render method, this way we don't have to re-bind every time the component re-renders.
constructor(props) {
super(props);
// constructor code
this.rotateCircle = this.rotateCircle.bind(this);
}
// later: onClick={this.rotateCircle}
Step 3
This is the more complicated step, as we now have to delegate the heavy work to App instead of the individual Circles.
So App needs to know the angles of each individual circle, and handle what happens when each circle is clicked. Furthermore, when angles change, we want to check if all three of them are equal. If they are equal, we need to tell ClawCircle what image to render.
All in all, it would probably look something like this:
EDIT: I should have probably tried running this code before writing it on the fly here. Here's the full version (tested!) Just make sure you have claw-react claw-vue and claw-angular rules in your CSS
import React, { Component } from 'react';
import './App.css';
import { CSSTransitionGroup } from 'react-transition-group';
class HalfCircle extends Component {
render() {
return (
<div className="App-logo half-circle" alt="logo">
</div>
);
}
}
const Circleone = ({ currentAngle, onClick }) => (
<div
className="App-logo small-logo"
alt="logo"
style={{ transform: `rotateZ(${currentAngle}deg` }}
onClick={onClick}
>
<div className="little-circle one react"></div>
<div className="little-circle two angular"></div>
<div className="little-circle three vue"></div>
</div>
);
const Circletwo = ({ currentAngle, onClick }) => (
<div
className="App-logo big-logo"
alt="logo"
style={{ transform: `rotateZ(${currentAngle}deg` }}
onClick={onClick}
>
<div className="little-circle un react"></div>
<div className="little-circle dos angular"></div>
<div className="little-circle tres vue"></div>
</div>
);
const Circlethree = ({ currentAngle, onClick }) => (
<div
className="App-logo biggest-logo"
alt="logo"
style={{ transform: `rotateZ(${currentAngle}deg` }}
onClick={onClick}
>
<div className="little-circle ein react"></div>
<div className="little-circle zwei angular"></div>
<div className="little-circle drei vue"></div>
</div>
);
class ClawCircle extends Component {
constructor(props){
super(props)
this.state = {
currentAngle: 45,
anglePerClick: 360,
}
}
rotateCircle() {
const { currentAngle, anglePerClick } = this.state;
this.setState({
currentAngle: currentAngle + anglePerClick
})
}
render() {
const circleStyle = {
transform: `rotateZ(${this.state.currentAngle}deg)`
}
return (
<div
className={`App-logo claw-circle ${this.props.imageName}`}
alt="logo"
style={circleStyle}
onClick={this.rotateCircle.bind(this)}
/>
);
}
}
const getNameForAngle = (one, two, three) => {
if (one === two && one === three) {
switch(one) {
case 120:
return 'claw-react';
case 240:
return 'claw-vue';
case 360:
return 'claw-angular';
default:
return '';
}
}
return '';
};
class App extends Component {
constructor(props) {
super(props);
this.state = {
oneAngle: 120,
twoAngle: 120,
threeAngle: 120,
};
this.handleOneClick = this.handleOneClick.bind(this);
this.handleTwoClick = this.handleTwoClick.bind(this);
this.handleThreeClick = this.handleThreeClick.bind(this);
}
handleClick(circle) {
const nextAngle = this.state[circle] + 120;
this.setState ({
[circle]: nextAngle
});
}
handleOneClick() {
this.handleClick('oneAngle');
}
handleTwoClick() {
this.handleClick('twoAngle');
}
handleThreeClick() {
this.handleClick('threeAngle');
}
render() {
const { oneAngle, twoAngle, threeAngle } = this.state;
const imageName = getNameForAngle(oneAngle, twoAngle, threeAngle);
return (
<div className="App">
<header className="App-header">
<Circleone
currentAngle={oneAngle}
onClick={this.handleOneClick}
/>
<Circletwo
currentAngle={twoAngle}
onClick={this.handleTwoClick}
/>
<Circlethree
currentAngle={threeAngle}
onClick={this.handleThreeClick}
/>
<ClawCircle imageName={imageName} />
<HalfCircle/>
</header>
</div>
);
}
}
export default App;
Ok it seems that components encapsulation really disfavors this king of fun in here, anyway I got the app working with pure js, all hail global variables!
Here is the codepen if anyone needs it: https://codepen.io/Raitar/pen/OOWRzb
And of course the JS code:
var angle=0;
var angle2=0;
var angle3=0;
count = 0;
count2 = 0;
count3 = 0;
document.getElementById("small-logo").addEventListener("click", rotateCircle)
document.getElementById("big-logo").addEventListener("click", rotateCircle2)
document.getElementById("biggest-logo").addEventListener("click", rotateCircle3)
function rotateCircle(){
angle+=120;
this.style.webkitTransform="rotate("+angle+"deg)";
count += 1;
if (count > 2) {
count = 0;
}
}
function rotateCircle2(){
angle2+=120;
this.style.webkitTransform="rotate("+angle2+"deg)";
count2 += 1;
if (count2 > 2) {
count2 = 0;
}
}
function rotateCircle3(){
angle3+=120;
this.style.webkitTransform="rotate("+angle3+"deg)";
count3 += 1;
if (count3 > 2) {
count3 = 0;
}
}
angular = "background-image:
url(https://raw.githubusercontent.com/Blazej6/Door-
game/master/src/img/angular.png);"
react = "background-image:
url(https://raw.githubusercontent.com/Blazej6/Door-
game/master/src/img/react.png);"
vue = "background-image: url(https://raw.githubusercontent.com/Blazej6/Door-
game/master/src/img/vue.png);"
document.getElementById("claw-circle").addEventListener("click",
changeCenter)
var x = document.getElementById("claw-circle")
function changeCenter() {
if (count == 0 && count2 == 0 && count3 == 0) {
x.style.cssText = angular;
} else if(count == 1 && count2 == 1 && count3 == 1) {
x.style.cssText = react;
} else if(count == 2 && count2 == 2 && count3 == 2) {
x.style.cssText = vue;
}
}