Changing colours using custom CSS properties - resetting on refresh? (ReactJS) - javascript

I am trying to allow my admins to be able to change the branding and colours of my project, I am currently using CSS custom properties to do this, however, this doesn't seem to be saving. When I refresh the page it is refreshing the colours back to it's original state. Is there a way I can fix this?
CODE
JavaScript - Admin panel
import React, { Component } from 'react'
import './CompanyBranding.scss'
import firebase from 'firebase'
export default class CompanyBranding extends Component {
state = {
content: [],
textbox: "",
link: "",
primaryColour: "",
secondaryColour: ""
}
componentDidMount() {
firebase.firestore().collection('site').doc('public').collection('footer').doc('content').get().then(doc => {
const content = []
const data = doc.data()
this.setState(data)
content.push(data)
this.setState({content})
this.setState({contentLength: content.length})
})
}
handleChange = (e) => {
this.setState({
[e.target.id]: e.target.value,
})
}
handleSubmit = (e) => {
e.preventDefault();
firebase.firestore().collection('site').doc('public').collection('footer').doc('content').set({
textbox: this.state.textbox,
link: this.state.link
}).then(alert("Updated"),
document.getElementById("companydetails").reset()
)
document.documentElement.style.setProperty(
'--primary-color',
this.state.primaryColour
)
document.documentElement.style.setProperty(
'--secondary-color',
this.state.secondaryColour
)
}
render() {
return (
<div>
<div className="CompanyBranding">
<h1 className = "PageHeading"> Branding</h1>
<form className = "CompanyBrandingForm">
<label htmlFor = "Logo"> Company Logo</label> <br/>
<input type = "file" id = "Logo" name = "Logo" accept = "image/png, image/jpeg"/><br/><br/>
<label htmlFor = "PrimaryColour"> Primary Colour </label> <br/>
<input type = "color" id = "primaryColour" name = "PrimaryColour" onChange = {this.handleChange}/><br/><br/>
<label htmlFor = "SecondaryColour"> Secondary Colour </label> <br/>
<input type = "color" id = "secondaryColour" name = "SecondaryColour" onChange = {this.handleChange}/><br/><br/>
<button onClick = {this.handleSubmit} className = "SubmitButton"> Submit </button>
</form>
</div>
<div className="FooterContent">
<h1 className = "PageHeading"> Footer </h1>
<form className = "CompanyBrandingForm">
<label htmlFor = "textbox"> Text box</label> <br/>
<textarea id = "textbox" value = {this.state.textbox} onChange = {this.handleChange} />
<br/><br/>
<label htmlFor = "link"> GDPR link </label> <br/>
<input type = "text" id = "link" value = {this.state.link} onChange = {this.handleChange}/>
<button onClick = {this.handleSubmit} className = "SubmitButton"> Submit </button>
</form>
</div>
</div>
)
}
}
Variables.scss
:root {
--primary-color: #2f78e6;
--secondary-color:#2d4f81;
}
// Primary colour (can be changed by admin)
$base-colour: var(--primary-color);
$secondary-color: var(--secondary-color);

When you refresh the page your entire application is rerendered. So the color goes back to the default value as the state is lost. This means you need some kind of persistence.
Depending on what you're trying to achieve there are a few options:
If you want the admin to change the theme for everyone you should store that theme in some kind of database, for instance, Firestore as you are already using it. When you initialize the state, you run a query and fetch the value from Firestore.
If you want the admin to change the theme only for himself, you can set the preferred color in the localStorage and then fetch the value from it when you initialize the state.

Whatever style you change within React (or JS for that matter) will only be valid until the user refreshes or closes the page, because that's the scope of JavaScript.
If you want to create a theme functionality where users can choose their own theme, you should store their choice either in the localstorage or in the database (persistent storage). Only then, their choice will persist through refreshes. So, store the user's choice that you are getting from the form somewhere permanent (not in a React state).
And whenever the app loads, you have to fetch the user's choice from whichever persistent storage you used, and set the property (as you did) in CSS.

Related

can't append h1 element to parent div in React?

i'm creating a simple react website that's supposed to do some calculations and find out Joules of my input values after the calculations...right now the input values are already preset but i will remove the value="" from my <input> later.
here is the .JSX component file that's the issue...one of the components.
import React, { Component } from 'react';
import Atom_icon from './cartridges.png';
class Joule_calc extends Component {
render(){
return (
<div className='Joule_div'>
<h3 style={{color:"white", textAlign:"center"}}>JOULE CALCULATOR</h3>
<label className='lab1'>WEIGHT=/GRAMS</label><br></br>
<input className='weight_inp' type='text' value="2" />
<label className='lab2'>SPEED=M/S</label><br></br>
<input className='speed_inp' type='text' value="5" />
<button className='count_button' onClick={this.Create_response}>CALCULATE</button>
<h1 className='Result_joule'></h1>
</div>
)
}
Create_response(){
console.log("creating response...")
let sum = document.createElement("h1")
sum.className = 'Result_joule'
sum.textContent = "678"
let div_panel = document.getElementsByClassName("Joule_div")
div_panel.append('Result_joule')
}
Returned_values(){
let weight_val = document.getElementsByClassName("weight_inp")[0].value;
let speed_val = document.getElementsByClassName("speed_inp")[0].value;
let final_calculation = weight_val * speed_val
return final_calculation
}
}
export default Joule_calc
so when i run my code i get
Uncaught TypeError: div_panel.append is not a function
at Create_response (Joule_calc_window.jsx:31:1)
i don't get why i can't append my new element to the div. it says it's not a function so what's the solution then? i'm new to React and web so probably it's just a noobie thing.
also i tried directly creating a h1 inside the 'Joule_div' like this.
<h1 className='Result_joule'>{"((try returning here from one of these methods))"}</h1>
but that of course failed as well. So would appreciate some help to get what's going on. i'm trying to add a number after the button click that's in h1 and in future going to be a returned number after calculating together the input values in a method.i imagine that something like
MyMethod(){
value = values calculated
return value
}
and later grab it with this.MyMethod
example
<h1>{this.MyMethod}</h1>
this is a example that of course didn't work otherwise i wouldn't be here but at least gives you a clue on what i'm trying to do.
Thank you.
You don't leverage the full power of react. You can write UI with only js world thanks to JSX. State changes triggering UI update.
I may miss some specificaiton, but fundamental code goes like the below. You should start with function component.
// Function component
const Joule_calc = () =>{
// React hooks, useState
const [weight, setWeight] = useState(0)
const [speed, setSpeed] = useState(0)
const [result,setResult] = useState(0)
const handleCalculate = () =>{
setResult(weight*speed)
}
return (
<div className="Joule_div">
<h3 style={{ color: 'white', textAlign: 'center' }}>JOULE CALCULATOR</h3>
<label className="lab1">WEIGHT=/GRAMS</label>
<br></br>
<input className="weight_inp" type="text" value={weight} onChange={(e)=>setWeight(parseFloat(e.target.value))} />
<label className="lab2">SPEED=M/S</label>
<br></br>
<input className="speed_inp" type="text" value={speed} onChange={(e)=>setSpeed(parseFloat(e.target.value))} />
<button className="count_button" onClick={handleCalculate}>
CALCULATE
</button>
<h1 className='Result_joule'>{result}</h1>
</div>
)
}
export default Joule_calc;
div_panel is an collection of array which contains the classname ["Joule_div"]. so first access that value by using indexing . and you should append a node only and your node is "sum" not 'Result_joule' and you should not use textcontent attribute because you will be gonna definitely change the value of your result as user's input value
Create_response(){
console.log("creating response...")
let sum = document.createElement("h1")
sum.className = 'Result_joule'
//sum.textContent = "678"
let div_panel = document.getElementsByClassName("Joule_div")
div_panel[0].append('sum')
}
if any problem persists , comment below

REACT-HOOKS: How do I store a modifed parameter made by the user?

I have checkboxes and I want to save in useState hooks the modified value made by the user. By default the current state is fixed and the checkbox is filled if my_value === 1, elif 0 unfilled. But if my user decides to uncheck it, how can I store this action. (if unchecked the value is 0).
Same idea with dropdown, the default value is fixed. The user can change the Taste( Good/Medium/Bad)or the Comments ((0/4....4/4)).
For now I get only the current state.
export default function Display() {
...
//For my checkboxes
const [availability, setAvailability] = useState(item.values[0].availability)
...
const [trust, setTrust] = useState(item.values[0].trust)
//For my dropdowns
const [taste, setTaste] = useState(item.taste)
...
const [comments, setComments] = useState(rule.comments)
function Checkbox({ value }) {
const [checked, setChecked] = useState(value);
return (
<label>
<input
type="checkbox"
checked={checked}
onChange={() => setChecked(checked => !checked)}
/>
{value}
</label>
);
}
return (
<div>
<div>
Availability : <Checkbox value={!!availability} />
</div>
....
<div >
Taste : <Dropdown style={styles.select} options={TASTE} defaultValue={LIKELIHOOD.find((t) => t.label === item.taste)} />
</div>
...
</div >
);
}
This isn't so much a hooks problem as a "where do I store my state" problem. So far I don't see any place in your implementation to store the users choices. Either the MenuItemDisplay component needs to maintain that state, or it needs to receive it from a parent component. Either way, that state (containing user choices) will need to be passed down (along with update functions) into the checkbox component as the value of a 'checked' prop, and the update functions for that state should be passed as (and adapted to) the checkbox 'onToggle' (or similar) prop

Showing Results using child when clicking a button in parent component

I am Trying to build a simple trivia quiz using React and i am stuck on as to how to show the results when the user clicks "Check Answer" Button. I need to change the colors of label reflecting correct and wrong choices.
props ={
questions: arr[str]
answers: arr[arr[str]]
correct-answer: "str"
}
export default function QuestionPage(props){let [showResults,setShowResults] = React.useState(false)let Questions = []
// create 5 questions using the data from props
function getQuestions(){
for (let i =0;i<props.questions.length;i++){
Questions.push(
<Question
key= {nanoid()}
question = {props.questions[i]}
answers = {shuffle(props.answers[i])}
correct_answer = {props.correct_answers[i]}
showResults = {showResults}
/> )
}
return Questions
}
function TotalScore(){
Questions.forEach( (value,index)=>{
console.log(value,"\t",index)
})
//get all inputs using DOM and check
}
return (
<main>
{getQuestions()}
{ showResults && <TotalScore />}
<button onClick={() => setShowResults(true)}>Check Answers</button>
</main>
)
}
I was hoping to change the background color of labels inside the input field I have created for answering the questions :
Red if user selected the wrong option
Green if correct.
I can handle the css bit of coloring, just need to understand "how to implement the passing of command from button in parent component down to child component" functionality in React.
Below is the code for Question.js Component
export default function Question ({question,answers,correct_answer,showResults}) {
const [selected,setSelected] = React.useState({
selected: ""
})
function handleChange(event){
console.log(event.target,"\t",selected.selected)
setSelected( {
selected: event.target.value}
)
}
return(
<div className='question-container'>
<h4>{question}</h4>
<div className='answers-row'>
<fieldset>
<input
type="radio"
id = {answers[0]}
name = {question}
value = {answers[0]}
onChange = {handleChange}
checked = {selected.selected === answers[0]}
/>
<label htmlFor={answers[0]}>{answers[0]}</label>
<br />
<input
type="radio"
id={answers[1]}
name = {question}
value = {answers[1]}
onChange = {handleChange}
checked= {selected.selected === answers[1]}
/>
<label htmlFor={answers[1]}>{answers[1]}</label>
<br />
<input
type="radio"
id={answers[2]}
name = {question}
value = {answers[2]}
onChange = {handleChange}
checked= {selected.selected === answers[2]}
/>
<label htmlFor={answers[2]}>{answers[2]}</label>
<br />
<input
type="radio"
id={answers[3]}
name = {question}
value = {answers[3]}
onChange = {handleChange}
checked= {selected.selected === answers[3]}
/>
<label htmlFor={answers[3]}>{answers[3]}</label>
<br />
</fieldset>
</div>
</div>
)
}
In your code, you are maintaining the state ie. the answer selected by the user, on each Question component. However, when the user clicks the button QuestionPage components re-render so do its children. The getQuestions() will be invoked and components will be created again and states will again be initialized to the default value ie "".
Read this https://reactjs.org/docs/lifting-state-up.html. The example given here is similar to what you are trying to do.

Updating a field inside a firebase firestore doc without hand write the field key to update Angular

How could i update a doc inside my firestore without precisely write in the code the field i want to update , cause already is reached through the form:
.Lets say the HTML tag in Angular already brings both the key and the import(that one to be updated):
HTML Tags
<form [formGroup]="importNgForm" (submit)="addUniqueImport()">
<div class="modal-body">
<div class="form-group">
<label for="key"></label>
<input disabled type="text" value='{{incomeSelected}}' name="key" class="form-control" formControlName="key" />
</div>
<div class="form-group">
<label for="import">Add amount</label>
<input type="number" name="import" class="form-control" formControlName="import" />
</div>
</div>
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Add</button>
</div>
......more code
Then on my component for that html :
some imports...
export class UserSheetBalanceComponent implements OnInit {
importNgForm: FormGroup;
constructor(
private service: Service,
private amountBuilder:FormBuilder,
) {
this.importNgForm = this.amountBuilder.group({
key:new FormControl(),
import:new FormControl(),
});
}
addUniqueImport() {
this.service.addingImport(this.importNgForm.value as addImport)
}
and then finally on my service component i just try to pass the parameters the form brings :
addingImport(dataToPass: addImport) {
const path = this.docCreator
.collection('users')
.doc(this.userdata.uid)
.collection('Incomings')
.doc(this.userdata.uid);=====>Path reaching the doc to update
Until declaring the path to access that document where the field is.But then when try to refer the name of the field i want to update through the form (dataToPass.key) ,and the import for this field im doing reference to (dataToPass.import) the error appears.
path.update({dataToPass.key:dataToPass.import}) ====>doesn't work
}
The problem is in the key, let say i instead of accessing my form(dataToPass) i write directly the name of the field to update(some name),i does work
path.update({some name:dataToPass.import}) ======>does work
}
so im wondering how could i access that field key without precisely write it , but dynamically, in order to update my import once the field on query matchs
Thanks in advance!!!!
if you have reference to object in firebase
const path = this.docCreator
.collection('users')
.doc(this.userdata.uid)
.collection('Incomings')
.doc(this.userdata.uid);
you can crete empty object and use key name from dataToPass.key to set property
let foo: any = {};
foo[`${dataToPass.key}`] = dataToPass.import;
path.update(foo);

Passing html element value to composeWithTracker in React

This Meteor code uses React. When user fills in an input box with id myVal, click a button. The input box value gets sent to the server via a method, the server updates the collection vehicles.
It then needs to take that input from user and use that as query to collection.findOne in myfile.jsx. It failed to pass the user input myVal from html input element.
How can it be done? Thanks
// -------------------- myfile.jsx -------------------
const renderWhenData = ( cars ) => {
if ( cars ) {
return <span>{ cars.description }</span>;
}
};
const Info = ( { cars } ) => (
<p>{ renderWhenData( cars ) }</p>
);
const composer = (props, onData) => {
const subscription = Meteor.subscribe('vehicles');
if (subscription.ready()) {
let myVal = document.getElementById('myVal').value;
console.log(myVal); // <------------------ nothing is printed out
const cars = Vehicles.findOne({name: myVal});
onData(null, {cars});
}
};
const Container = composeWithTracker(composer)(Info);
ReactDOM.render(<Container />, document.getElementById('react-info'));
// --------------------- events.js -----------------------
document.getElementById('startButton').addEventListener('click', () => {
const myVal = document.getElementById('myVal').value;
Meteor.call('startInfo', myVal); // <---------- updates server collection
});
<!--main.html-->
<head>
</head>
<body>
<form action="submit">
<input type="text" id="myVal">
<div id="react-info"></div>
</form>
<footer>
<button id="startButton">START</button>
</footer>
</body>
The question is a bit academic. There are better ways to handle this in React. But, since you are using Meteor, this can be done with some Tracker operations.
First define a tracker dependency:
export const textboxDep = new Tracker.Dependency;
Whenever the textbox value changes, trigger the changed event.
textboxDep.changed();
Within the composer, use
textboxDep.depend();
If the composer is written well, when the tracker dependency is invalidated, the whole functional container component runs again. And you should see the value of the textbox there.
Right now, when the value of the textbox is changed, since it is not reactive, the container does not re-render itself.

Categories

Resources