Estou tentando converter esta <canvas>
animação legal que encontrei aqui em um componente reutilizável React. Parece que esse componente exigiria um componente pai para a tela e muitos componentes filhos para o function Ball()
.
Por motivos de desempenho, provavelmente seria melhor transformá- Balls
los em componentes sem estado, pois haverá muitos deles. Não estou tão familiarizado com a criação de componentes sem estado e queria saber onde devo definir as funções this.update()
e this.draw
que são definidas em function Ball()
.
As funções para componentes sem estado ficam dentro ou fora do componente? Em outras palavras, qual das opções a seguir é melhor?
1:
const Ball = (props) => {
const update = () => {
...
}
const draw = () => {
...
}
return (
...
);
}
2:
function update() {
...
}
function draw() {
...
}
const Ball = (props) => {
return (
...
);
}
Quais são os prós / contras de cada um e um deles é melhor para casos de uso específicos como o meu?
Respostas:
A primeira coisa a notar é que os componentes funcionais sem estado não podem ter métodos, você não deve contar com a chamada
update
oudraw
com um renderizadoBall
se for um componente funcional sem estado.Na maioria dos casos, você deve declarar as funções fora da função do componente para declará-las apenas uma vez e sempre reutilizar a mesma referência. Quando você declara a função interna, toda vez que o componente for renderizado, a função será definida novamente.
Há casos em que você precisará definir uma função dentro do componente para, por exemplo, atribuí-lo como um manipulador de eventos que se comporta de maneira diferente com base nas propriedades do componente. Mesmo assim, você pode definir a função externamente
Ball
e vinculá-la às propriedades, tornando o código muito mais limpo e tornando as funçõesupdate
oudraw
reutilizáveis.// You can use update somewhere else const update (propX, a, b) => { ... }; const Ball = props => ( <Something onClick={update.bind(null, props.x)} /> );
Se estiver usando ganchos , você pode usar
useCallback
para garantir que a função só seja redefinida quando uma de suas dependências (props.x
neste caso) mudar:const Ball = props => { const onClick = useCallback((a, b) => { // do something with a, b and props.x }, [props.x]); return ( <Something onClick={onClick} /> ); }
Este é o caminho errado :
const Ball = props => { function update(a, b) { // props.x is visible here } return ( <Something onClick={update} /> ); }
Ao usar
useCallback
, definir aupdate
função nouseCallback
próprio gancho ou fora do componente torna-se uma decisão de design mais do que qualquer coisa, você deve levar em consideração se for reutilizarupdate
e / ou se precisar acessar o escopo do fechamento do componente para, por exemplo, ler / gravar no estado. Pessoalmente, eu escolho defini-lo dentro do componente por padrão e torná-lo reutilizável apenas se for necessário, para evitar o excesso de engenharia desde o início. Além disso, a reutilização da lógica do aplicativo é melhor realizada com ganchos mais específicos, deixando os componentes para fins de apresentação. Definir a função fora do componente durante o uso de ganchos realmente depende do grau de desacoplamento do React que você deseja para a lógica do aplicativo.fonte
this.draw
função dentro doBall
. Ele usa actx
partir do que seria do pai<canvas>
e também usa athis
palavra - chave para o que seria oBall
componente filho . Qual seria a melhor maneira de integrar a implementação do componente sem estado para que ambas as propriedades sejam acessíveis?this
quando usar componentes funcionais sem estado, tenha isso em mente. Para o contexto do canvas, você teria que passá-lo para cada umBall
, isso não soa bem.bind
especificamente no Chrome anterior ao 59 era ainda mais lento do que as funções de seta. E no Firefox já demorou bastante, já que ambos funcionam com a mesma velocidade. Então eu diria que nesses casos não há diferença de qual é a forma preferida :)Something
componente saber que existe um prop X em seu componente pai, pois o torna ciente de seu contexto. O mesmo acontece com a pergunta que você está fazendo e com o código de amostra que escrevi, depende do contexto, que é ignorado por uma questão de simplicidade.Podemos ter funções dentro de componentes funcionais sem estado, abaixo está o exemplo:
const Action = () => { function handlePick(){ alert("test"); } return ( <div> <input type="button" onClick={handlePick} value="What you want to do ?" /> </div> ); }
Porém, não é uma boa prática, pois a função
handlePick()
será definida sempre que o componente for chamado.fonte
handlePick()
acima / fora do componente comofunction handlePick() { ... }; const Action = () => { ... }
o resultado sendo que handlePick é definido apenas uma vez. Se você precisar de dados doAction
componente, deve passá-los como parâmetros para ahandlePick
função.Podemos usar o gancho React
useCallback
como abaixo em um componente funcional:const home = (props) => { const { small, img } = props const [currentInd, setCurrentInd] = useState(0); const imgArrayLength = img.length - 1; useEffect(() => { let id = setInterval(() => { if (currentInd < imgArrayLength) { setCurrentInd(currentInd => currentInd + 1) } else { setCurrentInd(0) } }, 5000); return () => clearInterval(id); }, [currentInd]); const onLeftClickHandler = useCallback( () => { if (currentInd === 0) { } else { setCurrentInd(currentInd => currentInd - 1) } }, [currentInd], ); const onRightClickHandler = useCallback( () => { if (currentInd < imgArrayLength) { setCurrentInd(currentInd => currentInd + 1) } else { } }, [currentInd], ); return ( <Wrapper img={img[currentInd]}> <LeftSliderArrow className={currentInd > 0 ? "red" : 'no-red'} onClick={onLeftClickHandler}> <img src={Icon_dir + "chevron_left_light.png"}></img> </LeftSliderArrow> <RightSliderArrow className={currentInd < imgArrayLength ? "red" : 'no-red'} onClick={onRightClickHandler}> <img src={Icon_dir + "chevron_right_light.png"}></img> </RightSliderArrow> </Wrapper>); } export default home;
Estou pegando 'img' de seu pai e isso é uma matriz.
fonte
Se você quiser usar adereços ou estado de componente em função, isso deve ser definido em componente com useCallback.
function Component(props){ const onClick=useCallback(()=>{ // Do some things with props or state },[]) }
Por outro lado, se você não quiser usar adereços ou estado em função, defina isso fora do componente.
const computeSomethings=()=>{ // Do some things with params or side effects } function Component(props){ }
fonte