Considerando el siguiente ejemplo de ganchos
import { useState } from 'react';
function Example() {
const [count, setCount] = useState(0);
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
Básicamente usamos este método.forceUpdate () para forzar al componente a volver a renderizarse inmediatamente en los componentes de la clase React como el siguiente ejemplo
class Test extends Component{
constructor(props){
super(props);
this.state = {
count:0,
count2: 100
}
this.setCount = this.setCount.bind(this);//how can I do this with hooks in functional component
}
setCount(){
let count = this.state.count;
count = count+1;
let count2 = this.state.count2;
count2 = count2+1;
this.setState({count});
this.forceUpdate();
//before below setState the component will re-render immediately when this.forceUpdate() is called
this.setState({count2: count
}
render(){
return (<div>
<span>Count: {this.state.count}></span>.
<button onClick={this.setCount}></button>
</div>
}
}
Pero mi consulta es ¿Cómo puedo forzar al componente funcional anterior a volver a renderizar inmediatamente con ganchos?
javascript
reactjs
react-native
react-hooks
Hemadri Dasari
fuente
fuente
this.forceUpdate()
? Tal vez haya una forma de lograr lo mismo sin eso.Respuestas:
Esto es posible con
useState
ouseReducer
, ya que seuseState
usauseReducer
internamente :const [, updateState] = React.useState(); const forceUpdate = React.useCallback(() => updateState({}), []);
forceUpdate
no está diseñado para usarse en circunstancias normales, solo en pruebas u otros casos pendientes. Esta situación se puede abordar de una manera más convencional.setCount
es un ejemplo de uso incorrectoforceUpdate
,setState
es asincrónico por razones de rendimiento y no se debe forzar a ser sincrónico solo porque las actualizaciones de estado no se realizaron correctamente. Si un estado se basa en el estado establecido previamente, esto debe hacerse con la función de actualización ,setCount
Puede que no sea un ejemplo ilustrativo porque su propósito no está claro, pero este es el caso de la función de actualización:setCount(){ this.setState(({count}) => ({ count: count + 1 })); this.setState(({count2}) => ({ count2: count + 1 })); this.setState(({count}) => ({ count2: count + 1 })); }
Esto se traduce 1: 1 a ganchos, con la excepción de que las funciones que se utilizan como devoluciones de llamada deben memorizarse mejor:
const [state, setState] = useState({ count: 0, count2: 100 }); const setCount = useCallback(() => { setState(({count}) => ({ count: count + 1 })); setState(({count2}) => ({ count2: count + 1 })); setState(({count}) => ({ count2: count + 1 })); }, []);
fuente
const forceUpdate = useCallback(() => updateState({}), []);
funciona? ¿Incluso fuerza una actualización?useCallback
memorizaforceUpdate
, por lo que se mantiene constante durante la vida útil de los componentes y se puede pasar como un accesorio de forma segura.updateState({})
actualiza el estado con un nuevo objeto en cadaforceUpdate
llamada, esto da como resultado una re-renderización. Entonces sí, fuerza una actualización cuando se llama.useCallback
parte no es realmente necesaria. Debería funcionar bien sin él.updateState(0)
porque0
es un tipo de datos primitivo? ¿Debe ser un objeto o también funcionaríaupdateState([])
(es decir, el uso con una matriz)?updateState(Math.random())
, o un contador.Como han mencionado los demás,
useState
funciona (así es como mobx-react-lite implementa las actualizaciones), podría hacer algo similar.Definir un nuevo gancho,
useForceUpdate
-import { useState, useCallback } from 'react' export function useForceUpdate() { const [, setTick] = useState(0); const update = useCallback(() => { setTick(tick => tick + 1); }, []) return update; }
y usarlo en un componente -
const forceUpdate = useForceUpdate(); if (...) { forceUpdate(); // force re-render }
Consulte https://github.com/mobxjs/mobx-react-lite/blob/master/src/utils.ts y https://github.com/mobxjs/mobx-react-lite/blob/master/src/useObserver .ts
fuente
useForceUpdate
que devolverá una nueva función cada vez que la función vuelva a renderizarse. ParaforceUpdate
que funcione cuando se usa en auseEffect
, debe regresaruseCallback(update)
Ver kentcdodds.com/blog/usememo-and-usecallbackGeneralmente, puede utilizar cualquier enfoque de manejo de estado que desee para activar una actualización.
Con TypeScript
ejemplo de codesandbox
useState
const forceUpdate: () => void = React.useState()[1].bind(null, {}) // see NOTE below
useReducer
const forceUpdate = React.useReducer(() => ({}), {})[1] as () => void
como gancho personalizado
Simplemente envuelva el enfoque que prefiera así
function useForceUpdate(): () => void { return React.useReducer(() => ({}), {})[1] as () => void // <- paste here }
¿Cómo funciona esto?
" Activar una actualización " significa decirle al motor React que algún valor ha cambiado y que debería volver a procesar su componente.
[, setState]
fromuseState()
requiere un parámetro. Nos deshacemos de él atando un objeto nuevo{}
.() => ({})
inuseReducer
es un reductor ficticio que devuelve un objeto nuevo cada vez que se envía una acción.{}
(objeto nuevo) es necesario para que active una actualización cambiando una referencia en el estado.PD:
useState
solo se envuelveuseReducer
internamente. fuenteNOTA: El uso de .bind con useState provoca un cambio en la referencia de función entre las representaciones. Es posible envolverlo dentro de useCallback como ya se explicó aquí , pero entonces no sería un sexy one-liner ™ . La versión Reducer ya mantiene la igualdad de referencia entre los renders. Esto es importante si desea pasar la función forceUpdate en props.
JS simple
const forceUpdate = React.useState()[1].bind(null, {}) // see NOTE above const forceUpdate = React.useReducer(() => ({}))[1]
fuente
Alternativa a la respuesta de @ MinhKha:
Puede ser mucho más limpio con
useReducer
:const [, forceUpdate] = useReducer(x => x + 1, 0);
Uso:
forceUpdate()
- limpiador sin paramsfuente
Simplemente puede definir el useState así:
const [, forceUpdate] = React.useState(0);
Y uso:
forceUpdate(n => !n)
Espero que esto ayude !
fuente
Preferiblemente, solo debe hacer que su componente dependa del estado y los accesorios y funcionará como se espera, pero si realmente necesita una función para obligar al componente a volver a renderizarse, puede usar el
useState
gancho y llamar a la función cuando sea necesario.Ejemplo
const { useState, useEffect } = React; function Foo() { const [, forceUpdate] = useState(); useEffect(() => { setTimeout(forceUpdate, 2000); }, []); return <div>{Date.now()}</div>; } ReactDOM.render(<Foo />, document.getElementById("root"));
<script src="https://unpkg.com/[email protected]/umd/react.production.min.js"></script> <script src="https://unpkg.com/[email protected]/umd/react-dom.production.min.js"></script> <div id="root"></div>
fuente
forceUpdate()
y solo leer desdethis.props
y haciathis.state
adentrorender()
".and I can't think of a good use case for it right now
tengo uno, ¿qué tal si el estado no está controlado por React? No uso Redux, pero supongo que debe tener algún tipo de actualización forzada. Yo personalmente uso proxy para mantener el estado, el componente puede luego verificar los cambios de prop y luego actualizar. Parece que también funciona de manera muy eficiente. Por ejemplo, todos mis componentes son controlados por un proxy, este proxy está respaldado por SessionStorage, por lo que incluso si el usuario actualiza su página web, se mantiene el estado de los menús desplegables, etc. IOW: No uso el estado en absoluto, todo se controla con accesorios.Solución oficial de React Hooks FAQ para
forceUpdate
:const [_, forceUpdate] = useReducer((x) => x + 1, 0); // usage <button onClick={forceUpdate}>Force update</button>
Ejemplo de trabajo
Mostrar fragmento de código
const App = () => { const [_, forceUpdate] = useReducer((x) => x + 1, 0); return ( <div> <button onClick={forceUpdate}>Force update</button> <p>Forced update {_} times</p> </div> ); }; ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.10.1/umd/react.production.min.js" integrity="sha256-vMEjoeSlzpWvres5mDlxmSKxx6jAmDNY4zCt712YCI0=" crossorigin="anonymous"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.10.1/umd/react-dom.production.min.js" integrity="sha256-QQt6MpTdAD0DiPLhqhzVyPs1flIdstR4/R7x4GqCvZ4=" crossorigin="anonymous"></script> <script>var useReducer = React.useReducer</script> <div id="root"></div>
fuente
Código simple
const forceUpdate = React.useReducer(bool => !bool)[1];
Utilizar:
fuente
Puede (ab) usar ganchos normales para forzar una repetición aprovechando el hecho de que React no imprime booleanos en código JSX
// create a hook const [forceRerender, setForceRerender] = React.useState(true); // ...put this line where you want to force a rerender setForceRerender(!forceRerender); // ...make sure that {forceRerender} is "visible" in your js code // ({forceRerender} will not actually be visible since booleans are // not printed, but updating its value will nonetheless force a // rerender) return ( <div>{forceRerender}</div> )
fuente
La opción potencial es forzar la actualización solo en el uso de componentes específicos
key
. La actualización de la clave desencadena una representación del componente (que no se pudo actualizar antes)Por ejemplo:
const [tableKey, setTableKey] = useState(1); ... useEffect(() => { ... setTableKey(tableKey + 1); }, [tableData]); ... <DataTable key={tableKey} data={tableData}/>
fuente
Solución en una sola línea:
const [,forceRender] = useReducer((s) => s+1, 0)
Puede aprender sobre useReducer aquí. https://reactjs.org/docs/hooks-reference.html#usereducer
fuente
Solución de una línea:
const useForceUpdate = () => useState()[1];
useState devuelve un par de valores: el estado actual y una función que lo actualiza - state y setter , aquí estamos usando solo el setter para forzar la re-renderización.
fuente
Mi variación de
forceUpdate
no es a través de uncounter
sino más bien a través de un objeto:// Emulates `forceUpdate()` const [unusedState, setUnusedState] = useState() const forceUpdate = useCallback(() => setUnusedState({}), [])
Porque
{} !== {}
cada vez.fuente
useCallback()
? De donde vino eso? Ups. Lo veo ahora ...Esto representará los componentes dependientes 3 veces (las matrices con elementos iguales no son iguales):
const [msg, setMsg] = useState([""]) setMsg(["test"]) setMsg(["test"]) setMsg(["test"])
fuente
Para componentes regulares basados en React Class, consulte React Docs para la
forceUpdate
API en esta URL. Los documentos mencionan que:Sin embargo, también se menciona en los documentos que:
Entonces, aunque los casos de uso para el uso
forceUpdate
pueden ser raros, y no lo he usado nunca, sin embargo, lo he visto usado por otros desarrolladores en algunos proyectos corporativos heredados en los que he trabajado.Por lo tanto, para conocer la funcionalidad equivalente de los componentes funcionales, consulte React Docs for HOOKS en esta URL. Según la URL anterior, se puede utilizar el gancho "useReducer" para proporcionar una
forceUpdate
funcionalidad para los componentes funcionales.A
that does not use state or props
continuación se proporciona un ejemplo de código de trabajo , que también está disponible en CodeSandbox en esta URLimport React, { useReducer, useRef } from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { // Use the useRef hook to store a mutable value inside a functional component for the counter let countref = useRef(0); const [, forceUpdate] = useReducer(x => x + 1, 0); function handleClick() { countref.current++; console.log("Count = ", countref.current); forceUpdate(); // If you comment this out, the date and count in the screen will not be updated } return ( <div className="App"> <h1> {new Date().toLocaleString()} </h1> <h2>You clicked {countref.current} times</h2> <button onClick={() => { handleClick(); }} > ClickToUpdateDateAndCount </button> </div> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);
NOTA: En esta URL también se encuentra disponible un enfoque alternativo que utiliza el gancho useState (en lugar de useReducer) .
fuente
react-tidy
tiene un gancho personalizado solo para hacer eso llamadouseRefresh
:import React from 'react' import {useRefresh} from 'react-tidy' function App() { const refresh = useRefresh() return ( <p> The time is {new Date()} <button onClick={refresh}>Refresh</button> </p> ) }
Más información sobre este gancho
Descargo de responsabilidad Soy el autor de esta biblioteca.
fuente