How do i make a very basic useState hook in React? - reactjs

I'm trying to make a very basic useState hook in React, but cant figure out what im doing wrong.
This is a simple useState that changes the text inside the paragraph to whatever you write in the textfield.
export default function Demo() {
const [value, setValue] = React.useState();
const handleValue= () => {
setValue(value)
}
return(
<>
<TextField onChange={handleValue} />
<p>{value}</p>
</>
)
}
The paragraph doesn't render anything. Did I miss something?

Your handleChange function is currently setting the value to itself. You must assign it to the value obtained from the TextField.
Here is a working example.
const {
useState,
useEffect,
Fragment
} = React;
function Demo() {
const [value, setValue] = useState();
const handleValue = ({target}) => {
setValue(target.value);
}
return <Fragment>
<input type="text" onChange={handleValue}/>
<p>{value}</p>
</Fragment>;
}
const el = document.querySelector("#root");
ReactDOM.render(<Demo/>, el);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script>
<div id="root"></div>

You'd need to handle the value coming out of the change handler and use it to update your state 😀
export default function Demo() {
const [value, setValue] = React.useState();
const handleValue= (e) => {
setValue(e.target.value)
}
return(
<>
<TextField onChange={handleValue} />
<p>{value}</p>
</>
)
}

Related

React, why state doesn't change?

It's a simple example about derived state.
Here is the sandbox url: https://codesandbox.io/s/review-react-derived-state-2-forked-v9ucpr?file=/src/App.js
I expected value change 0 to 10, when I click the button. But, it doesn't rendering also, its flow is weired.
I click button several times, it doesn't re-render, even though changing the value.
Here is the code:
import { useState, useEffect } from "react";
export default function App() {
console.log("App is called!");
const [value, setValue] = useState(() => {
console.log("App useState is alloc");
return 0;
});
console.log("App is END");
return (
<div>
<NumberInput value={value} onChange={setValue} />
<button onClick={() => setValue(10)}>change to ten</button>
</div>
);
}
function NumberInput({ value, onChange }) {
console.log(" NumberInput is called!");
const [inputValue, setInputValue] = useState(value);
useEffect(() => {
const numberValue = Number(inputValue);
onChange(numberValue);
}, [inputValue, onChange]);
console.log(" NumberInput is END");
return (
<input value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
);
}
Your
const [inputValue, setInputValue] = useState(value);
"forks" the prop value to local state, so changes to value won't be reflected in inputValue after initial mount.
You'd need
useEffect(() => setInputValue(value), [value]);
in the child component to mirror any changes to the value prop to inputValue too.
You are not updating the internal state of the NumberInput when the value changes.
This would be the code that solves the issue:
import { useState, useEffect } from "react";
export default function App() {
const [value, setValue] = useState(0);
return (
<div>
<NumberInput value={value} onChange={setValue} />
<button onClick={() => setValue(10)}>change to ten</button>
</div>
);
}
function NumberInput({ value, onChange }) {
const [inputValue, setInputValue] = useState(value);
useEffect(() => {
onChange(+inputValue);
}, [inputValue, onChange]);
// We add a use effect that will get trigger when the parent
// value changes
// However, this implies that the NumberInput will always render
// because of the previous useEffect, which also updates the
// value's value
useEffect(() => {
setInputValue(`${value}`);
}, [value]);
return (
<input value={inputValue} onChange={(e) => setInputValue(e.target.value)} />
);
}
useState will catch your value only on initialising the child component.
Try:
<input value={value}
// instead of
<input value={inputValue}
I hope that is what you wanted!

How to update ANTD text input field in React (like getElementById)

I need the equivalent way up updating a simple text input field like so
document.getElementById("myid").value = "sample input"
using React hooks, and the textfield is an Antd control.
Here is the code I have that doesn't work:
import { Input } from "antd"
import { useRef } from "react";
export default function App() {
const inputRef = useRef();
const myfunction = () => {
inputRef.current = "sample input"
}
return (
<div>
<button onClick={myfunction} >populate textbox</button>
<p/>
<Input ref={inputRef} />
</div>
);
}
You can try this code and read a doc for React.
And take a closer look at the attributes of the components that you take in antd;
const [value, setValue] = useState('');
const myfunction = () => {
setValue('Text')
}
return (
<>
<button onClick={myfunction} >populate textbox</button>
<Input value={value}>
</>
)

How to write a useComponent custom hook in React?

I want to create a custom hook useComponent which returns a JSX.Element that will be rendered elsewhere.
I have tried this:
import { useState} from 'react';
const useComponent = () => {
const [value, setValue] = useState('');
const c = () => {
return <>
<p>Component</p>
<input value={value} onChane={(e) => setValue(e.target.value)} />
</>
}
return {
c,
value,
}
}
export default function App() {
const {c: C} = useComponent();
return (
<div className="App">
<C />
</div>
);
}
but it does not work. Once I try typing on input, nothing happens.
How can I achieve this ?
I know it might be a bad practice to do such a thing, but the reason I want this is to be able to open a global dialog and pass the c component as children to the <Dialog /> component so I can both render c inside the dialog's body and also have access to the [value, setValue] state. So my use case would be something like:
[EDIT]
I also add the whole logic with dialog:
import { createContext, useContext, useState } from "react";
const Test = ({ value, setValue }) => {
return (
<>
<p>Component</p>
<input value={value} onChange={(e) => setValue(e.target.value)} />
</>
);
};
const useComponent = () => {
const [value, setValue] = useState("");
return {
element: <Test value={value} setValue={setValue} />,
value
};
};
const DialogCTX = createContext({});
export function DialogProvider(props) {
const [component, setComponent] = useState(null);
const ctx = {
component,
setComponent
};
return (
<DialogCTX.Provider value={ ctx }>
{props.children}
</DialogCTX.Provider>
);
}
export const useDialog = () => {
const {
component,
setComponent,
} = useContext(DialogCTX);
return {
component,
setComponent,
}
};
const Dialog = () => {
const { component } = useDialog();
return <div>
<p>Dialog</p>
{component}
</div>
}
const Setter = () => {
const {element, value} = useComponent();
const {setComponent} = useDialog();
return <div>
<p>Setter component</p>
<p>{value}</p>
<button onClick={() => setComponent(element)}>Set</button>
</div>
}
export default function App() {
return <div className="App">
<DialogProvider>
<Setter />
<Dialog />
</DialogProvider>
</div>;
}
As you said you want to return a JSX.Element but you actually returning a new component (a new function) every time your hook runs. So you could achieve your goal if you actually declare your component outside your hook and return the rendered one. Here is a working example:
import { useState } from "react";
const Test = ({ value, setValue }) => {
return (
<>
<p>Component</p>
<input value={value} onChange={(e) => setValue(e.target.value)} />
</>
);
};
const useComponent = () => {
const [value, setValue] = useState("");
return {
element: <Test value={value} setValue={setValue} />,
value
};
};
export default function App() {
const { element } = useComponent();
return <div className="App">{element}</div>;
}

useEffect with local variable

I am trying to call useEffect funtion onchange of local variable, but its not working is only works if i use it with useState variable, I know there might be some basic thing here that I am not aware of.
sandbox link: https://codesandbox.io/s/affectionate-gareth-igyv7?file=/src/demo.js
import React, { useEffect, useState } from "react";
import "./styles.css";
export default function Demo() {
const [value, setValue] = useState("");
let valueOne, valueTwo;
const setValueOne = (value) => {
valueOne = value;
};
useEffect(() => {
console.log(value);
console.log(valueOne);
}, [value, valueOne]);
return (
<div>
<h1>Demo</h1>
<input
placeholder="useState"
onChange={(e) => setValue(e.target.value)}
/>
<input
placeholder="function"
onChange={(e) => setValueOne(e.target.value)}
/>
{/* {console.log(valueOne)} */}
</div>
);
}
setValueOne will not rerender your component, If you want to fire a re-render, useEffect function needs to have a useState which basically hold state between re-renders.
You can try managing your state like below, its more readable and it will work too.
import React, { useState } from "react";
import "./styles.css";
export default function Demo() {
const [valueOne, setValueOne] = useState("");
const [valueTwo, setValueTwo] = useState("");
const handleValueOne = (e) => {
setValueOne(e.target.value);
};
const handleValueTwo = (e) => {
setValueTwo(e.target.value);
};
return (
<div>
<h1>Demo</h1>
<input
value={valueOne}
placeholder="useState"
onChange={handleValueOne}
/>
<input
value={valueTwo}
placeholder="function"
onChange={handleValueTwo}
/>
{/* {console.log(valueOne)} */}
</div>
);
}

How to focus something on next render with React Hooks

I'm playing with hooks, and I'm trying to do the following:
import React, { useState, useRef } from 'react';
const EditableField = () => {
const [isEditing, setEditing] = useState(false);
const inputRef = useRef();
const toggleEditing = () => {
setEditing(!isEditing);
if (isEditing) {
inputRef.current.focus();
}
};
return (
<>
{isExpanded && <input ref={inputRef} />}
<button onClick={toggleEditing}>Edit</button>
</>
);
};
This is going to fail, because current is null, since the component haven't re-rendered yet, and the input field is not yet rendered (and therefore can't be focused yet).
What is the right way to do this? I can use the usePrevious hook proposed in the React Hooks FAQ, but it seems like a painful workaround.
Is there a different way?
You can use the useEffect hook to run a function after every render when isEditing changed. In this function you can check if isEditing is true and focus the input.
Example
const { useState, useRef, useEffect } = React;
const EditableField = () => {
const [isEditing, setEditing] = useState(false);
const toggleEditing = () => {
setEditing(!isEditing);
};
const inputRef = useRef(null);
useEffect(() => {
if (isEditing) {
inputRef.current.focus();
}
}, [isEditing]);
return (
<div>
{isEditing && <input ref={inputRef} />}
<button onClick={toggleEditing}>Edit</button>
</div>
);
};
ReactDOM.render(<EditableField />, document.getElementById("root"));
<script src="https://unpkg.com/react#16.7.0-alpha.2/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom#16.7.0-alpha.2/umd/react-dom.production.min.js"></script>
<div id="root"></div>
I know the accepted answer covers the requested element in the above question.
But as an additional note, if you are using functional components, make use of React.forwardRef to pass down the reference to child components. It might be
definitely useful for someone who refers to this question later on.
In a more cleaner way, you can write your child component which accept the ref as given below:
const InputField = React.forwardRef((props, ref) => {
return (
<div className={props.wrapperClassName}>
<input
type={props.type}
placeholder={props.placeholder}
className={props.className}
name={props.name}
id={props.id}
ref={ref}/>
</div>
)
})
Or Simply use this component
import { FC, useEffect, useRef } from 'react'
export const FocusedInput: FC<JSX.IntrinsicElements['input']> = (props) => {
const inputRef = useRef<null | HTMLElement>(null)
useEffect(() => {
inputRef.current!.focus()
}, [])
return <input {...props} type="text" ref={inputRef as any} />
}

Resources