React Native open only a specific block [duplicate] - reactjs

I have a question, if I can use useState generic in React Hooks, just like I can do this in React Components while managing multiple states?
state = {
input1: "",
input2: "",
input3: ""
// .. more states
};
handleChange = (event) => {
const { name, value } = event.target;
this.setState({
[name]: value,
});
};

Yes, with hooks you can manage complex state (without 3rd party library) in three ways, where the main reasoning is managing state ids and their corresponding elements.
Manage a single object with multiple states (notice that an array is an object).
Use useReducer if (1) is too complex.
Use multiple useState for every key-value pair (consider the readability and maintenance of it).
Check out this:
// Ids-values pairs.
const complexStateInitial = {
input1: "",
input2: "",
input3: ""
// .. more states
};
function reducer(state, action) {
return { ...state, [action.type]: action.value };
}
export default function App() {
const [fromUseState, setState] = useState(complexStateInitial);
// handle generic state from useState
const onChangeUseState = (e) => {
const { name, value } = e.target;
setState((prevState) => ({ ...prevState, [name]: value }));
};
const [fromReducer, dispatch] = useReducer(reducer, complexStateInitial);
// handle generic state from useReducer
const onChangeUseReducer = (e) => {
const { name, value } = e.target;
dispatch({ type: name, value });
};
return (
<>
<h3>useState</h3>
<div>
{Object.entries(fromUseState).map(([key, value]) => (
<input
key={key}
name={key}
value={value}
onChange={onChangeUseState}
/>
))}
<pre>{JSON.stringify(fromUseState, null, 2)}</pre>
</div>
<h3>useReducer</h3>
<div>
{Object.entries(fromReducer).map(([key, value]) => (
<input
name={key}
key={key}
value={value}
onChange={onChangeUseReducer}
/>
))}
<pre>{JSON.stringify(fromReducer, null, 2)}</pre>
</div>
</>
);
}
Notes
Unlike the setState method found in class components, useState does not automatically merge update objects. You can replicate this behavior by combining the function updater form with object spread syntax:
setState(prevState => {
// Object.assign would also work
return {...prevState, ...updatedValues};
});
Refer to React Docs.

The correct way to do what you're trying to do is to create your own hook that uses useState internally.
Here is an example:
// This is your generic reusable hook.
const useHandleChange = (initial) => {
const [value, setValue] = React.useState(initial);
const handleChange = React.useCallback(
(event) => setValue(event.target.value), // This is the meaty part.
[]
);
return [value, handleChange];
}
const App = () => {
// Here we use the hook 3 times to show it's reusable.
const [value1, handle1] = useHandleChange('one');
const [value2, handle2] = useHandleChange('two');
const [value3, handle3] = useHandleChange('three');
return <div>
<div>
<input onChange={handle1} value={value1} />
<input onChange={handle2} value={value2} />
<input onChange={handle3} value={value3} />
</div>
<h2>States:</h2>
<ul>
<li>{value1}</li>
<li>{value2}</li>
<li>{value3}</li>
</ul>
</div>
}
ReactDOM.render(<App />, document.querySelector("#app"))
<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="app"></div>
Note the use of React.useCallback to stop your hook from returning a new handler function on every render. (We don't need to specify setValue as a dependency because React guarantees that it will never change)

I didn't actually test this, but it should work.
See https://reactjs.org/docs/hooks-reference.html#usestate for more info.
import React, {useState} from 'react';
const MyComponent = () => {
const [name, setName] = useState('Default value for name');
return (<div><button onClick={()=>setName('John Doe')}}>Set Name</button></div>);
};
export default MyComponent;

Related

React Context children re-rendering to initial state when updating context state

I'm having an issue where, whenever I update my context state within a hook (useEffect, useCallback, etc.), any other state updates that I make don't actually go through because the state for that component resets to initial state.
I have the following component which provides the message context, its provider, and exposes a hook to access the message state variable's value and setter:
const MessageContext = React.createContext<MessageContextProps>({
message: {} as IMessage,
setMessage: () => {},
});
export function MessageProvider(props: MessageProviderProps): JSX.Element {
const [message, setMessage] = useState<IMessage>({} as IMessage);
return <MessageContext.Provider value={{ message, setMessage }}>{props.children}</MessageContext.Provider>;
}
export function useMessage(): MessageContextProps {
return React.useContext(MessageContext);
}
My app is wrapped in the provider:
ReactDOM.render(
<React.StrictMode>
<ErrorBoundary>
<MessageProvider>
<App />
</MessageProvider>
</ErrorBoundary>
</React.StrictMode>,
document.getElementById('root')
);
Whenever I try to update the formData in my component (setFormData(newFormData)) in the same render cycle as I update the message state (setMessage()) from MessageContext, only the message state updates. The rest of my state updates don't re-render (specifically, the formData.changedValue value in the final paragraph tag), I think because it's getting reset to initial state.
export default function App(): JSX.Element {
const [formData, setFormData] = useState(INITIAL_STATE);
const { message, setMessage } = useMessage();
const [updateSuccessful, setUpdateSuccessful] = useState(false);
const handleSubmit = useCallback(
(event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
makeSomeAPICall(formData);
setUpdateSuccessful(true);
},
[formData]
);
useEffect(() => {
if (updateSuccessful) {
setFormData((current) => {
return {
...current,
changedValue: current.myField,
};
});
setMessage({
displayText: `Change Successful`,
type: 'success',
});
}
}, [updateSuccessful]);
return (
<>
<form onSubmit={handleSubmit}>
<div>
<select id='changeMe' value={formData.myField} onChange={() => setFormData(formData.myField)}>
<option value='Y'>Y</option>
<option value='N'>N</option>
</select>
<button type='submit'>Submit</button>
</form>
<p>Changed Value: {formData.changedValue}</p>
</>
);
}

using createselector still rerenders component

I learn about redux and its features. One thing that I get trouble with is createselector in reduxtoolkit. I have a slice:
const titlesSlice = createSlice({
name: "title",
initialState: {
titles: [],
title: "",
},
reducers: {
addTitle: (state, action) => {
state.titles.push({
id: Math.trunc(Math.random() * 10000).toString(),
title: state.title,
});
},
titleChange: (state, action) => {
state.title = action.payload;
},
},
});
and a selectors like:
const getTitles = (state) => (state.titles.titles);
export const selectTitlesLengthWithReselect = createSelector(
[getTitles],
(titles) => titles.filter(elem => elem.title.length > 5))
In App.js I added input for adding title:
function App(props) {
const dispatch = useDispatch();
const title = useSelector((state) => state.titles.title);
return (
<div className="App">
<div>
<input type="text"
onChange={(e) => dispatch(titleChange(e.target.value))}
value={title} />
<button onClick={() => dispatch(addTitle())}>Save</button>
<Titlelist />
</div>
</div>
);
}
TitleList component:
const Titlelist = () => {
const allTitles = useSelector(selectTitlesLengthWithReselect);
console.log("RENDERED");
return (
<div>
{allTitles.map((elem) => (
<li key={elem.id}>{elem.title}</li>
))}
</div>
)
}
Problem is every time input value(title in the titleReducer) changes the TitleList component rerenders. But the data that comes from selector is memoized(I checked for the prev and current value equality and they are same). Is there something that I'm doing wrong or why does component rerenders?
Nothing wrong with your selector, it is memoized, the selector did not cause the re render (re creating of jsx).
Dispatching titleChange will re render App so it will re render Titlelist because it is not a pure component, you can make it a pure component with React.memo:
const Titlelist = React.memo(function TitleList() {
const allTitles = useSelector(
selectTitlesLengthWithReselect
);
console.log('RENDERED');
return (
<div>
{allTitles.map((elem) => (
<li key={elem.id}>{elem.title}</li>
))}
</div>
);
});
As phry commented; it is usually fine to let your component re create jsx as React will do a virtual DOM compare and not re render DOM if the jsx is the same, if you have handlers that are re created (like: onClick={()=>new ref}) then that will fail virtual DOM compare. You can use useCallback to prevent that.
A pure component will generate the same jsx reference if no props changed so will have a quicker virtual dom compare but will also take up more memory and setup processing so it may not always benefit your application.

React Functional Component - Binding handler to an array

I am trying to bind a handler to an array position that prints the state of the object. The function is being called but the state is always in the initial state.
import React from "react";
export default function MyForm() {
const [state, setState] = React.useState({
buttons: [],
object: null
});
const stateRef = React.useRef(null);
const onExecButtonHandler = (index) => {
console.log("[MyForm][execButton]: " + index);
alert(JSON.stringify(state.object));
alert(JSON.stringify(stateRef.current.object));
};
const onChangeHandler = (event)=>{
let obj = state.object
obj['description'] = event.target.value
console.log(obj)
setState((state) => ({ ...state, object: obj }));
stateRef.current = state;
}
const generateButtons = () => {
let buttons = [];
//buttons.push([name,(event)=>onExecButtonHandler(event,i),obj[i].icon,obj[i].jsFunction])
buttons.push([
"Show Hi",
onExecButtonHandler.bind(this, 1),
"icon",
"custom function"
]);
return buttons;
};
React.useEffect(() => {
console.log("[MyForm][useEffect]");
let buttons = generateButtons();
setState({ buttons: buttons, object: { description: "hi" } });
stateRef.current = state;
}, []);
return (
<>
{state.object && (
<form>
<input text="text" onChange={onChangeHandler} value={state.object.description} />
<input
type="button"
value="Click me"
onClick={() => {
state.buttons[0][1]();
}}
/>
</form>
)}
</>
);
}
You can test here: https://codesandbox.io/s/charming-robinson-g1yuo?fontsize=14&hidenavigation=1&theme=dark
I was able to access the latest state of the object using the "useRef" hook. I'd like to access using the state though. Is there some way to do so?

Render data in React Hooks

Hello I'm try to Render data in a component to another component, which are siblings to one another. with useState Hook(rff) based on component code (rcf)
index.js -> is entry point, that calls only one component App, as we have no route
App.js -> is the parent component, which has two child, Certification and Panel
Certification.js -> takes input
Panel -> renders data from certification
I know i have problem with handleFromCert (this is my handle change function)
here my code -rff
https://codesandbox.io/s/zen-paper-gil-xcyj3?file=/src/
here the code that based on rcf and work fine
https://codesandbox.io/s/elegant-shtern-362ki?file=/src/
I corrected the code and now it works!
handleFromCert in App.js should receive name and value;
value2 in the Panel component in App.js is passed with an error;
handleFromCert in Certifications.js setValue changes incorrectly.
Certifications.js
import React, { useState } from "react";
const Certifications = (props) => {
const [value, setValue] = useState({
value1: "",
value2: ""
});
const handleFromCert = ({ target: { value, name } }) => {
setValue(prevState => ({ ...prevState, [name]: value }));
props.handleFromCert(name, value);
};
return (
<div>
<input name="value1" onChange={handleFromCert} />
<input name="value2" onChange={handleFromCert} />
<div>
Inside certificate
<div>{value.value1}</div>
<div>{value.value2}</div>
Certificate ends
</div>
</div>
);
};
export default Certifications;
App.js
import React, { useState } from "react";
import Certifications from "./Certifications";
import Panel from "./Panel";
const App = () => {
const [value, setValue] = useState({
value1: "",
value2: ""
});
const handleFromCert = (name, value) =>
setValue((prevState) => ({ ...prevState, [name]: value }));
return (
<div>
{value.value1}
{value.value2}
<Certifications handleFromCert={handleFromCert} />
<Panel value1={value.value1} value2={value.value2} />
</div>
);
};
export default App;
The problem is that you're not passing the event as the argument, you're passing the value and your function is expecting the event. In your Certification component change this:
const handleFromCert = (e) => {
setValue({
[e.target.name]: e.target.value
});
props.handleFromCert((e.target.name, e.target.value));
};
To this:
const handleFromCert = (e) => {
setValue({
[e.target.name]: e.target.value
});
props.handleFromCert(e);
};
Your function handleFromCert is expecting the event, and you're just passing the value which is a string and cannot be destructured like the event.
Here's the sandbox working for me:
https://codesandbox.io/s/zen-paper-gil-forked-r01fh

input element's onChange property using useState hook

I'm having trouble with controlled react input element. In class components, we used to have a handler like this, on onChange:
handleChange(event) {
const { name, value } = event.targeet
this.setState({
[name]: value
});
}
But in hooks, how do I achieve the same? There is no name property defined here.
import React, { useState } from "react"
function App() {
const [inputValue, setValue] = useState("reactjs")
return (
<div>
<input
value={inputValue}
onChange=
/>
</div>
)
export default App
}
You will need to store your state in an object, like so:
import React, { useState } from "react"
function App() {
const [state, setState] = useState({})
function handleChange(event) {
const { name, value } = event.target
setState({
...state,
[name]: value
})
}
return (
<div>
<input
name="input1"
value={state["input1"]}
onChange={handleChange}
/>
<input
name="input2"
value={state["input2"]}
onChange={handleChange}
/>
</div>
)
}
Note, setState will set the entire state, not merging with the existing state, so you have to explicitly merge the new state with the old state ({...state, [name]: value}).
One way of achieving this with hooks could be to put all of your inputs values in a single state, just like you used to do it.
function App() {
const [inputsValue, setValues] = useState({})
return (
<div>
<input
value={inputsValue[name]}
onChange={({ target: { name, value }}) => setValues(old => { ...old, [name]: value })}
/>
</div>
)
export default App
}
You can then set a specific value by deconstructing the odl state value, and modiying the one you want with computed properties :
setValues(old => { ...old, [name]: value })

Resources