I have a large project with multiple files. I want a button to render the content of some other components upon clicking. I am trying to understand why the following does not work, and what could I do to make it work instead?
Index.js
import React from "react";
import ReactDOM from "react-dom";
import { unitState, setUnitState } from './changeUnit.js'
function App() {
return (
<div>
<h1>{unitState}</h1>
<button onClick={setUnitState("°C")}>°C</button>
<button onClick={setUnitState("°F")}>°F</button>
</div>
);
}
// ------
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
changeUnit.js
export let unitState = "°C";
export function setUnitState(unit) {
(unit==="°C") ? unitState="°C" : unitState="°F";
}
with codesandbox link here
Currently, upon clicking any of the buttons the text does not change. But the code does reach inside the setUnitState method. I suspect that is because the main component is not rerendering. I tried to change my changeUnit.js code to
import App from './index.js';
export let unitState = "°C";
export function setUnitState(unit) {
(unit==="°C") ? unitState="°C" : unitState="°F";
App.forceUpdate();
}
but I get the error _index.default.update is not a method.
I have tried to use the useState hook from React but I just can't make it work for the life of me. I have tried many combinations but it seems that I can't return the setState function as a return from the custom hook.
Does anyone know how I can solve this problem?
I'm not sure why you're not using the useState hook, or what went wrong with your custom hook.
With useState():
function App() {
let [unitState, setUnitState] = React.useState('°C')
return (
<div>
<h1>{unitState}</h1>
<button onClick={() => setUnitState('°C')}>°C</button>
<button onClick={() => setUnitState('°F')}>°F</button>
</div>
);
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
With custom hook:
function App() {
let { unitState, setCelsius, setFahrenheit } = useSwitch()
return (
<div>
<h1>{unitState}</h1>
<button onClick={setCelsius}>°C</button>
<button onClick={setFahrenheit}>°F</button>
</div>
);
}
function useSwitch() {
let [unitState, setUnitState] = React.useState('°C')
let setCelsius = () => setUnitState('°C')
let setFahrenheit = () => setUnitState('°F')
return { unitState, setCelsius, setFahrenheit }
}
ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.1/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I'm guessing you probaly weren't using useState right. this is fairly simple with use state.
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
// import { unitState, setUnitState } from "./changeUnit.js";
function App() {
const [unitState, setUnitState] = useState("°C");
return (
<div>
<h1>{unitState}</h1>
<button onClick={() => setUnitState("°C")}>°C</button>
<button onClick={() => setUnitState("°F")}>°F</button>
</div>
);
}
// ------
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
I think you should just use setState, it might be the case that you were trying to call the setState without the lambda?
import React, { useState } from "react";
import ReactDOM from "react-dom";
function App() {
const [unitState, setUnitState] = useState("C");
return (
<div>
<h1>{unitState}</h1>
<button onClick={() => setUnitState("C")}>C</button>
<button onClick={() => setUnitState("F")}>F</button>
</div>
);
}
// ------
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
https://codesandbox.io/s/changeunitstate-forked-zbrym
Related
I'm trying to reference a React icon through a ref, and attempting to do so yields to error:
Function components cannot be given refs. Attempts to access this ref will fail. Did you
mean to use React.forwardRef()?
Is there any way to work around this? I tried wrapping the icon in a React.forwardRef() to no avail.
import React, { useRef } from 'react'
import { FaChevronDown } from "react-icons/fa"
export default function Dashboard() {
const chevronRef = useRef(null)
const ChevronIconWrapper = React.forwardRef((props, ref) =>(
<FaChevronDown className="icon" ref={ref} />
));
const AccountLabel = () => {
{/* Neither of these seem to work. */}
<FaChevronDown className="icon" ref={chevronRef} />
<ChevronIconWrapper ref={chevronRef}/>
}
return(
<AccountLabel />
)
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
I have a hook, and 2 components. Component App.js has a function that changes the state in hook, but the value is not updated in Component New.js, why? I think I've missed something but can't figure it out.
App.js
export const useToggle = () => {
const [onOff, setOnOff] = useState(false);
return [onOff, () => setOnOff((prev) => !prev)];
};
export default function App() {
const [onOff, setOnOff] = useToggle();
return (
<div className="App">
<h1>{onOff.toString()}</h1>
<button onClick={setOnOff}>toggle</button>
</div>
);
}
New.js
import { useToggle } from "./App.js";
export default function New() {
const [onOff] = useToggle();
return (
<div className="App">
<hr />
<h1>NEW:</h1>
<pre>{onOff.toString()}</pre>
</div>
);
}
https://codesandbox.io/s/musing-fire-rjude?file=/src/App.js
Each useToggle hook is its own entity with its own state. The useToggle that you are toggling in App isn't the same useToggle that is rendered/used in New.
This means they are toggled independently of any other hooks and state. They don't share "state".
If you are wanting to create a useToggle hook that does have shared state then I would suggest implementing it via a React context and the useContext hook so each useToggle hook can toggle the same shared state held in the context.
Update
Global useToggle hook.
togglecontext.js
import { createContext, useContext, useState } from 'react';
export const ToggleContext = createContext([false, () => {}]);
const ToggleProvider = ({ children }) => {
const [onOff, setOnOff] = useState(false);
const toggle = () => setOnOff(t => !t);
return (
<ToggleContext.Provider value={[onOff, toggle]}>
{children}
</ToggleContext.Provider>
);
}
export const useToggle = () => useContext(ToggleContext);
export default ToggleProvider;
index - provide the context
...
import ToggleProvider from "./toggle.context";
const rootElement = document.getElementById("root");
ReactDOM.render(
<StrictMode>
<ToggleProvider>
<App />
<New />
</ToggleProvider>
</StrictMode>,
rootElement
);
App
import "./styles.css";
import { useToggle } from "./toggle.context";
export default function App() {
const [onOff, setOnOff] = useToggle();
return (
<div className="App">
<h1>{onOff.toString()}</h1>
<button onClick={setOnOff}>toggle</button>
</div>
);
}
New
import { useToggle } from "./toggle.context";
export default function New() {
const [onOff] = useToggle();
return (
<div className="App">
<hr />
<h1>NEW:</h1>
<pre>{onOff.toString()}</pre>
</div>
);
}
Note that the only thing that changed in the App and New components was the import, where the useToggle hook is defined.
I have 3 components (CompA, CompB, CompC) I want to send data to from CompA to CompC but it is showing error Render is not a function updateContextConsumer. I am using functional components
import React,{ createContext } from 'react';
import './App.css';
import CompA from './ContextApi/CompA';
const FirstName = createContext();
function App() {
return (
<>
<FirstName.Provider value={"JD"}>
<CompA/>
</FirstName.Provider>
</>
);
}
export default App;
export {FirstName};
import React from 'react';
import CompB from './CompB';
const CompA = () =>{
return(
<CompB/>
)
}
export default CompA;
import React from 'react';
import CompC from './CompC';
const CompB = () =>{
return(
<CompC/>
)
}
export default CompB;
import React from 'react';
import {FirstName} from '../App';
const CompC = () =>{
return(
<>
<FirstName.Consumer> {fname=>(<h1>{fname}</h1>) } </FirstName.Consumer>
</>
)
}
export default CompC;
Image of error is here
enter image description here
I believe the issue is the spaces between the end of <FirstName.Consumer> and the { and the } and </FirstName.Consumer>. The following does not work with those spaces left in:
const FirstName = React.createContext();
const CompA = () =>{
return (
<CompB/>
)
};
const CompB = () =>{
return (
<CompC/>
)
};
const CompC = () => {
return(
<React.Fragment>
<FirstName.Consumer>{ (fname) => (<h1>{fname}</h1>) }</FirstName.Consumer>
</React.Fragment>
)
};
function App() {
return (
<React.Fragment>
<FirstName.Provider value={"JD"}>
<CompA/>
</FirstName.Provider>
</React.Fragment>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
<script crossorigin src="https://unpkg.com/react#17/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom#17/umd/react-dom.development.js"></script>
<div id="root"></div>
You could also put the { (fname) => (<h1>{fname}</h1>) } part on a separate line like shown here: https://reactjs.org/docs/context.html#contextconsumer
I'm facing the ReactJS routing issue. Working Demo. I'm using the hookrouter package.
Issue:
As mentioned in the demo, if I'm on the /users route and when I click on about link, the URL changes but About Component is not getting loaded.
What I want?
Is there a way to load a Component when I click on its link?
import { A, useRoutes } from "hookrouter";
const Users = () => {
return (
<div>
<h1>Users</h1>
About
</div>
);
};
const About = () => {
return (
<div>
<h1>About</h1>
Users
</div>
);
};
const routes = {
"/users": () => <Users />,
"/about": () => <About />
};
function App() {
const routeResult = useRoutes(routes);
return (
<div className="App">
<div>
Users Page
</div>
<div>
About Page
</div>
{routeResult}
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<React.StrictMode><App /></React.StrictMode>, rootElement);
There is an open issue in hookrouter library where if you wrap your app with <React.StrictMode> the navigations doesn't work.
So, for now, remove the strict mode and you will be fine.
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from "./App";
const rootElement = document.getElementById("root");
ReactDOM.render(
// <React.StrictMode> // <------ comment this for now.
<App />,
// </React.StrictMode>,
rootElement
);
working copy of your codesandbox
I was trying to add a css class on mouse over just as mentioned in the react documentation but I am not sure why it is not working.
Please help
index.js:
import React from "react";
import ReactDOM from "react-dom";
import "./styles.css";
function App() {
let className = "";
return (
<div className="App">
<h1
className={className}
onMouseOver={() => {
if (!className.includes("colorRed")) {
className += " colorRed";
}
}}
>
Hello CodeSandbox
</h1>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
style.css
.App {
font-family: sans-serif;
text-align: center;
}
.colorRed{
color:red;
}
Link: https://codesandbox.io/s/xjx72w5kkw?fontsize=14
This issue here is that you're mutating the className variable but this alone doesn't cause React to re-render your component.
You'll need to store the className in the state so that when you change it, React knows to re-render your component.
Here's a version of your sandbox making use of state: https://codesandbox.io/s/m51qoq72pp
// First we're importing useState from react
// This is part of the new Hooks mechanism
// More about hooks: https://reactjs.org/docs/hooks-intro.html
// You could also use a class and this.setState instead
import React, { useState } from "react";
// Then we set up our piece of state
const [className, setClassName] = useState("");
// Your event now uses setState
onMouseOver={() => {
if (!className.includes("colorRed")) {
setClassName(current => current + " colorRed");
}
}}