an external button to modify a component in react - reactjs

i have read and practice with a tutorial but i have tried to make something else and i can t. i explain:
in my app i have have two component <ColoredBlock /> whose code is:
class ColoredBlock extends React.Component {
constructor(props) {
super(props);
this.changeColor = this.changeColor.bind(this);
this.state = {
color: 'red'
};
}
changeColor() {
const newColor = this.state.color === 'red' ? 'blue' : 'red';
this.setState({
color: newColor
});
}
render() {
return (
<div ref={this.maref} className="App-headers" style={{ height: '200px', width: '200px', backgroundColor: this.state.color}}>
<ChangeColorButton clickHandler={this.changeColor} currentColor={this.state.color}></ChangeColorButton>
</div>
)
}
}
export default ColoredBlock;
Each component <ColoredBlock /> has a button as a child to modify the background color and all is working fine but
i have created an external button. This button is not a parent and not a child of my component
i d like , when i click on my button modify the background of one of my element but i cant have access to my element... i know that getElementById doesn t work and i have tried with 'ref' but i can t. Some helps please?

You have to define color state and function to change this state somewhere in parent component of both ColoredBlock and your button and pass it down as props.
It might look ugly thought. That's when redux and all that stuff comes in handy. Also you can use React Context

Related

React change class attributes

I'm trying to change class attribute in a function, I have a button and I want to change the text as soon as the user clicks it.
I wrote a class and everything but when the user clicks the button nothing happens!
I console logged to see if the variables actually changed and they did so why does it happen?
Here is my code:
class App extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
buttonStatus: 'Post room',
value: '',
}
}
PostANewRoomScreen = () => {
let {data} = '';
return (
<View style={{padding: 15}}>
<TouchableOpacity
style={styles.button}
disabled={this.state.loading}
onPress={() => this.postroom(data)}
>
<Text style={{color: "white" , fontSize: hp('2.5%'), padding: 5}}>
{this.state.loading && <Animated.Image style={{height: hp('3%'), width: wp('2%')}} source={{uri: 'https://media0.giphy.com/media/JTVkOqJ1RyYEBnyoRb/giphy.gif'}} />}
{this.state.buttonStatus}
</Text>
</TouchableOpacity>
</View>
postroom = (value) => {
this.state.loading = true;
this.state.buttonStatus = "button clicked!";
}
this.state.loading = true;
this.state.buttonStatus = "button clicked!";
This does not re render component. You have to use useState Hook (in funcation component) or setState() (in class component) in order to update the state. I suggest converting component to functional component and use hooks to manage state.
const [buttonStatus, setButtonStatus] = useState('Post Room');
then use setButtonStatus(value) inside your postroom function.
Please refer react doc for useSate Hooks
Further, Your existing code has some issues. you are trying to access and mutate state of App component. if you need to share the component state try to lift the state up. If you need to pass properties to component use props.

Change all Components with className="theme" from green to blue on an event in React

In App.css, I have
.theme {
color: green;
}
And I have className="theme" scattered in multiple components.
Is there a way to change the theme color from green to blue on an event?
If not, how should I design my code?
Well, You can create 2 classes named .blue-theme and .green-theme
Whenever, some event occurs,
onClick = (themeCol) => {
this.setState({theme:thmeCol})
}
render(){
return(
<button onClick={()=>onClick('blue-theme')}>Blue theme</button>
<button onClick={()=>onClick('green-theme')}>Green theme</button>
<div className={this.state.theme}> Sample </div>
)
}
You can pass the value of theme.
you can try
const themeClass = "";
if (event-as-wanted){
themeClass="theme";
}
className={themeClass}
also you can use style insted of className in same file
const theme ={
color: '';
};
style={theme}
and change it with events like
if (event-as-wanted){
theme.color = "green";
}
You can conditionally render the <style/> tag to override style definition for the class in the whole document.
class App extends React.Component {
state = {
red: true,
}
render() {
return (
<div>
<div className="foo">Foo</div>
<button onClick={() => this.setState({ red: !this.state.red })}>
Toggle Red
</button>
{this.state.red && <style>{".foo { color: red }"}</style>}
</div>
);
}
}
Keep in mind that inside JSX tags, curly brackets will be picked up by the interpreter and may break the parser. To avoid that, you should put your CSS inside a string like in the example above.
Adding a <style/> tag to CSS document will override any equally specific CSS rules that came before that. Once the condition is no longer met, the style tag will be removed and the original styling will be restored.
in react.js just set the state of color to whatever and on a click event toggle the color
class App extends React.Component {
constructor(props) {
super(props);
this.state = {color: green};
this.changeColor = this.changeColor.bind(this);
}
changeColor() {
const newcolor = this.state.color == green ? blue : green;
this.setState({ color: newcolor});
}
render() {
return (
<div class="theme" style={{color: this.statecolor}}>
<button onClick={this.changeColor}>
</button>
//put all html withing the parent DOM of element with class theme accordingly or it wont render.
</div>
);
}
Make two class, .green-theme{
color:'green'} and similarly, blue theme.
Mantain a REDUX STATE, CURRENT_THEME. Upon event fire, change the redux state accordingly and everywhere, where you want to use CURRENT_THEME, use it using mapStateToProps.
I would rather try to use almost pure CSS solution:
in App.css
#App.first-theme .theme { color:green; }
#App.second-theme .theme { color:blue; }
in App's render:
<div id="App" className={this.state.currentTheme}>
<AnotherComponent1 />
<AnotherComponent2 />
</div>
All you need to do is to change this.state.currentTheme appropriately. You can even use prop injected from the redux.
Almost all other solutions posted here have the same flaw: you have to adapt all your components to use the theme. Using this solution, you are able to change app's appearance without additional code in your components.
Trust me, injecting the same property from redux store/react context for every component will give you headaches and a lot of unnecessary code.
You should also try to avoid generating additional <style> tags - you will end up having plenty of !important and HTML, logic, and CSS in one file. What a mess! Imagine, what would happen if you would like to use SCSS in the future...

ShouldComponentUpdate is set to false child components do not update if props change

My goal is to render a child component without re-rendering it's parent component.
So for example, App's state is passed as a prop straight to the Column component but Column is a child of Table and Table has ShouldComponentUpdate set to false (For example, table data didn't change..).
The problem.. if Apps state changes the Column component does not update.. unless ShouldComponentUpdate is set to true on the Table Component.. Is there anyway around this?
The documentation does say
Returning false does not prevent child components from re-rendering
when their state changes.
But doesnt mention if their props change..
For test purposes I've created a demo here https://codesandbox.io/s/k2072rkp7o
Preview of the code:
const Column = ({ isSelected, onClick, children }) => (
<div
style={{
backgroundColor: isSelected ? 'green' : 'red',
padding: '10px',
}}
onClick={onClick}
>
Column: {children}
</div>
);
const Row = ({children }) => (
<div
style={{
backgroundColor: 'teal',
padding: '10px'
}}
>
Row {children}
</div>
)
class Table extends React.Component {
shouldComponentUpdate() {
// There will be logic here to compare table data to see if its changed..
return false
}
render() {
return (
<div
style={{
backgroundColor: '#ccc',
padding: '10px'
}}>
Table {this.props.children}
</div>
)
}
}
class App extends React.Component {
constructor() {
super();
this.state = {
isSelected: false
};
}
render() {
return (
<Table>
<Row>
<Column
isSelected={this.state.isSelected}
onClick={() => this.setState({
isSelected: !this.state.isSelected
})}
/>
</Row>
</Table>
)
}
}
Consider a solution where you are setting a default state onload and updating state where there is interaction with your table appending an 'color-whateveryoulike' class to your columns. Props won't help you in this instance because we never want to update props, you're wanting to listen for state updates.
U can use Table component to be PureComponent, and that PureComponent internaly checks for changes.
just change class Table extends React.Component to class Table extends React.PureComponent and then delete
shouldComponentUpdate() {
// There will be logic here to compare table data to see if its changed..
return false
}
because, as i said, PureComponent does that internaly.
Read more at: PureComponent
But don't use it always, because it can have side efect to slow your app down if used to much for unnecessary things.
Ok, after more reading, it's not possible... Which isn't good when you are playing with tables and want to apply a style to a cell component without causing a re-render of the whole table... Will have to look into alternatives...
Heres the component lifecycle for anyone who has same problem..

the changed value does not show in Dom

I am learning React, in my test app, I make 2 identical sets of random colored array that will shuffle and change color every time I click on 'change color' button. however I can not seem to make the Dom updates my array colors even the values of colors change correctly.
import React from 'react';
class Card extends React.Component{
constructor(props){
super(props);
const {r,g,b}=this.props.card
this.state={
style:{
width:'100px',
height:'100px',
display:'inline-block',
backgroundColor:`rgb(${r},${g},${b})`
}
}
}
onClick=()=>{
const {r,g,b}=this.props.card
console.log('color values of the card with index',this.props.id ,' is: ', r,g,b)
}
render(){
const {style}=this.state
return (
<div style={style}>
<button onClick={this.onClick}>card test</button>
</div>
)
}
}
export default Card;
this is the picture of my problem
as you can see in the picture, the values change every time I click but the cards' color stay the same. however, it will work if I change the class based component into non class based component and set the style in render() instead of constructor, but I want to have a class component so I can pass the card I click to parent component.
Is onClick triggering something else as well? Otherwise, don't see what would change the card's values, since the onClick is just logging.
Assuming the card prop is somehow changing correctly, I believe your issue is that your card prop is updating, but the state is set in the constructor and never updated.
Rather then setting a style value in state, I would just change to calculating style in render.
render() {
const {r,g,b} = this.props.card
const style = {
width: '100px',
height: '100px',
display: 'inline-block',
backgroundColor: `rgb(${r},${g},${b})`
}
return (
<div style={style}>
<button onClick={this.onClick}>card test</button>
</div>
)
}
You generally do not what to keep state that is easily derived from props to avoid situations like this.

In React, Is it good practice to search for certain element in DOM?

Is it good to just specify className for element so i could find it later in the DOM through getElementsByClassName for manipulations?
Adding a class to find the DOM element? Sure you can do that, but refs are probably the better solution.
Manipulating the DOM element? That's an absolute no-go. The part of the DOM that is managed by React should not be manipulated my anything else but React itself.
If you come from jQuery background, or something similar, you will have the tendency to manipulate element directly as such:
<div class="notification">You have an error</div>
.notification {
display: none;
color: red;
}
.show {
display: block;
}
handleButtonClick(e) {
$('.notification').addClass('show');
}
In React, you achieve this by declaring what your elements (components) should do in different states of the app.
const Notification = ({ error }) => {
return error
? <div className="notification">You have an error</div>
: null;
}
class Parent extends React.Component {
state = { error: false };
render() {
return (
<div>
<Notification error={this.state.error} />
<button onClick={() => this.setState({ error: true })}>
Click Me
</button>
}
}
The code above isn't tested, but should give you the general idea.
By default, the state of error in Parent is false. In that state, Notification will not render anything. If the button is clicked, error will be true. In that state, Notification will render the div.
Try to think declaratively instead of imperatively.
Hope that helps.
When using React, you should think about how you can use state to control how components render. this.setState performs a rerender, which means you can control how elements are rendered by changing this.state. Here's a small example. I use this.state.show as a boolean to change the opacity of the HTML element.
constructor(props) {
super(props)
this.state = {
show: true
}
}
handleClick() {
this.setState({show: false})
}
render() {
const visibility = this.state.show ? 1 : 0
return (
<button style={{opacity: visibility} onClick={() => this.handleClick()}>
Click to make this button invisible
</button>
)
}

Resources