No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Poniendo en pr谩ctica las render props

9/19
Recursos

Aportes 24

Preguntas 20

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

Codigo de TodoList en App/Index.js

      <TodoList
        error={error}
        loading={loading}
        searchedTodos={searchedTodos}
        onError={() => <TodosError />}
        onLoading={() => <TodosLoading />}
        onEmptyTodos={() => <EmptyTodos />}
        render={todo => (
          <TodoItem
            key={todo.text}
            text={todo.text}
            completed={todo.completed}
            onComplete={() => completeTodo(todo.text)}
            onDelete={() => deleteTodo(todo.text)}
          />
        )}
      />

codigo de TodoList en TodoList/index.js

function TodoList(props) {
  return (
    <section className="TodoList-container">
      {props.error && props.onError()}
      {props.loading && props.onLoading()}
      {(!props.loading && !props.searchedTodos.length) && props.onEmptyTodos()}

      <ul>
        {props.searchedTodos.map(props.render)}
      </ul>
    </section>
  );
}

La explicacion es muy clara y se entiende todo, pero Siento que esta clase esta mal enfocada, todo lo que era composicion de componentes se perdio en esta clase, se pasaron props al componente sumandole las render props, quedo un componente mas complejo, esto es realmente util cuando el componente que recibe las render props puede manejar su propio estado

Creo q perdimos las composici贸n de componentes volvemos a 煤tilizar props馃槄馃槄馃槄馃槄馃槄馃槄

En mi humilde opini贸n esto es a帽adirle otro nivel de complejidad a la aplicaci贸n y/o al componente (adem谩s que utilizar este recurso puede sacrificar un poco le legibilidad del c贸digo) por lo que hay que saber muy bien cu谩ndo usarlo鈥
.
No me parece 贸ptimo que todo lo que se vaya a renderizar adentro de un componente se pase por render props. En d贸nde s铆 veo que puede ser necesario usarlo es cuando el children del componente **no nos es suficiente ** y por lo tanto necesitemos de hacer render de un componente en un lugar aparte de donde estar谩n los children鈥 Espero explicarme bien 馃槄馃榿

Desde mi punto de vista, dentro de TodoList el siguiente c贸digo no es v谩lido (o est谩 mal usado):

<ul>
          {props.children}
</ul>

.
驴Por qu茅? Porque no estamos definiendo a TodoList como una etiqueta que contenga hijos dentro de s铆. Por esta raz贸n, dentro de <ul> no debemos emplear props.children, sino el resultado de la ejecuci贸n de props.render, que s铆 son los elementos de nuestra lista.
.
Ser铆a as铆:

{<ul> { props.searchedTodos.map( props.render ) } </ul>}

Recomiendo leer la documentaci贸n, los ejemplos son bastante buenos aqu铆:

https://es.reactjs.org/docs/render-props.html

TodoList

function TodoList(props) {
    return (
        <section className="TodoList-container">
            {props.error && props.onError()}
            {props.loading && props.onLoading()}

            {(!props.loading && !props.searchedTodos.length) && props.onEmptyTodos()}

            {props.searchedTodos.map(props.render)}

            <ul>
                {props.children}
            </ul>
        </section>
    )
}

index.js

<TodoList
        error={error}
        loading={loading}
        searchedTodos={searchedTodos}
        onError={() => <TodosError />}
        onLoading={() => <TodosLoading />}
        onEmptyTodos={() => <EmptyTodos />}
        render={todo => (
          <TodoItem
            key={todo.text}
            text={todo.text}
            completed={todo.completed}
            onComplete={() => completeTodo(todo.text)} //Marcamos el todo como completado
            onDelete={() => deleteTodo(todo.text)} //Eliminamos en todo
            />
        )}
      />

En este caso es mejor el operador ternario ya que si ocurre un error este no va a mostrar los todos sino que simplemente renderizara el componente error

<section className="container">
  <ul className="todo-list">
    {error
      ? onError()
      : loading
      ? onLoading()
      : !searchedTodos.length
      ? onEmptyTodos()
      : searchedTodos.map(render)}
  </ul>
</section>

Para quienes no entendieron el shortcut del profe en la linea 12 del componente TodoList, lo que esta haciendo es una funcionalidad propia del m茅todo map(). Este tiene la capacidad de mandar a llamar una funci贸n (Esto se conoce como un callback) por cada elemento que se encuentre en un array, props.render trae consigo una render function. Al liberarse nos trae un componente TodoItem provando que haga esto por cada elemento que nos viende dentro searchedTodos.
Por esto es que se recomienda saber un poquito m谩s JavaScript antes de pasarse directamente a cualquier framework. Les dejo la documentaci贸n de MDN que nos habla al respecto sobre esta caracter铆stica.

El simbolito de pregunta se llama signo de interrogaci贸n.

Me gusto mucho esta clase, ahora tienen mucho sentido las cosas

Creo que el map deberia renderizarse dentro el ul por tema de semantica

import '../css/TodoList.css'


function TodoList(props) {
  return (
    <section className="todo-list-container">
      {props.error && props.onError()}
      {props.loading && props.onLoading()}

      {(!props.loading && !props.todosFiltered.length) && props.onEmptyTodos()}

      <ul>
        {props.todosFiltered.map(props.render)}
      </ul>
    </section>
  );
}


export { TodoList };

f1 + backspace + nombredelarchivo 鈥 este es un atajo para encontrar rapidamente los archivos,

Poniendo en pr谩ctica las render props

Para esto vamos a reescribir nuestro componente TodoList. Vamos a escribir todas las funcionalidades que ten铆a este componente usando las render props.
.

Vamos a App.js.
.

Aqu铆 haremos la maquetaci贸n usando nuestras render props:

	...
		<TodoList 
	    onError={()=> <TodoError />}
	    onLoading={()=> <TodoLoading />}
	    onEmptyTodos={()=> <EmpyTodos />}
	    render={
	      todo => (
	        <TodoItem
	          key={todo.text}
	          text={todo.text}
	          completed={todo.completed}
	          onComplete={() => completeTodo(todo.text)}
	          onDelete={() => deleteTodo(todo.text)}
	        />
	      )
	    }
	  />

TodoList.js:

/* Aqu铆 a帽adimos una clase y validamos su hay algo en props.error
si lo hay, renderizamos lo que haya en la funci贸n onError() */
function TodoList(props) {
  return (
    <section className='TodoList-container'>
      {props.error && props.onError()}
      <ul>
        {props.children}
      </ul>
    </section>
  )
}

Para que esto sea posible iremos a nuestro TodoList y recibiremos el error, en caso de que lo haya para cumplir con la condicional.
.

Esto mismo haremos con loading y con searchedTodos.

	<TodoList 
    error={error}
    loading={loading}
    searchedTodos={searchedTodos}

    ...
  />

Ahora vamos a seguir maquetando nuestro Todolist.js.

function TodoList(props) {
  return (
    <section className='TodoList-container'>
      {/* haremos lo mismo que hicimos con error con el resto de 
      funciones */}
      {props.error && props.onError()}
      {props.loading && props.onLoading()}
      {(!props.loading && !props.searchedTodos.length) && props.onEmptyTodos()}

      <ul>
        {props.searchedTodos.map(props.render)}
      </ul>    
		</section>
  )
}

En este punto tenemos lo que deseamos, de esta forma es como usamos nuestras render props. 驴Muy f谩cil verdad? :3 .

en mi opini贸n creo que dejamos <ul> sin uso he realizado lo siguiente espero no estar mal con temas de l贸gica y buenas practicas

TodoList

{ props.error && props.onError() }
            { props.loading && props.onLoading() }
            { (!props.loading && !props.searchedTodos.length && props.searchValue.length < 1) && props.onEmptyTodos() }
            { (props.searchedTodos.length <= 0 && props.searchValue.length >= 1) && props.onSearchNoFound() }
            <ul>
                { props.searchedTodos.map(todo => props.render(todo)) }
            </ul>

En mi caso aplique el patr贸n de dise帽o de las render props de otra manera. Les dejo el c贸digo por si a alguien le parece 煤til.

function App() {

const [todos, manageTodos] = useLocalStorage()  
const [search, setSearch] = useState('')
const [isOpenModal, setOpenModal] = useState(false)

useEffect( () => {
  manageTodos('search')
}, [])

  const uncompletedTodos = todos? todos.filter((todo) => !todo.isCompleted).length : 0
  const totalTodos = todos.length
  const searchedTodos = todos.filter( todo => todo.description.toLowerCase().includes(search.toLowerCase())  )

  const list = (
    <>
      { searchedTodos.map( todo =>          
                <TodoItem key={todo.id} todos={todos} manageTodos={manageTodos} />    
            )}
    </>
  )

  const handleOpenModal = () => {
    setOpenModal(true)
  }

  return (
      <div className="app-container">
        <Header>
          <Counter uncompletedTodos={uncompletedTodos} totalTodos={totalTodos}/>
          <Search search={search} setSearch={setSearch} />
        </Header>
        <TodoList list={list}/>
        <img alt="icono para a帽adir todos" className="plus-icon" src={plusIcon} onClick={handleOpenModal}/>
  
        { isOpenModal &&
          <Modal setOpenModal={setOpenModal}>
            <NewTodo setOpenModal={setOpenModal} manageTodos={manageTodos} noOfTodos={todos.length}/>
          </Modal>
        }
      </div>
    
  )

}

El componente App contiene el estado de los Todos (que inicializo con un useEffect), desde App renderizo un componente todoList, que renderiza TodoItem, y a su vez este renderiza un TodoItemUI (con la maquetaci贸n de cada ToDo).
Entonces, para evitar el drilling cree una variable llamada list, en donde est谩 la iteraci贸n sobre los todos. Esto me permite desde el componente App mandarle a TodoItem las props, y finalmente desde TodoList recibo list como prop y la renderizo.

const TodoList = ( {list} ) => {
    return (
        <ul>
            {list}
        </ul>
    )
}

Me encanto este curso

tip si el archivo es .jsx en lugar de js, podemos usar un motor de completado para react, dependiendo del motor este pede no activarse si el archivo es .js

C贸digo de la clase en TypeScript
(Difiere un poco en organizacion con la del profe, pero lo importante es el tipado)

import { FC } from 'react'

import { TodoButton } from "../TodoButton"
import { TodoCounter } from "../TodoCounter"
import { TodoItem } from "../TodoItem"
import { TodoList } from "../TodoList"
import { TodoSearch } from "../TodoSearch"
import { TodosError } from "../TodoError"
import { EmptyTodos } from "../EmptyTodo"
import { TodosLoading } from "../TodoLoading"
import { Modal } from "../Modal"
import { TodoForm } from "../TodoForm"
import { TodoHeader } from "../TodoHeader"
import { useTodos } from './useTodos'

const App: FC = () => {

  const {
    loading,
    error,
    searchedTodos,
    addTodo,
    completeTodo,
    deleteTodo,
    openModal,
    setOpenModal,
    completedTodos,
    totalTodos,
    searchValue,
    setSearchValue,
  } = useTodos();

  return (
    <>
      <TodoHeader>
        <TodoCounter
          completedTodos={completedTodos}
          totalTodos={totalTodos}
        />
        <TodoSearch
          searchValue={searchValue}
          setSearchValue={setSearchValue}
        />
      </TodoHeader>
      <TodoList
        loading={loading}
        error={error}
        onLoading={()=>(
          <>
            <TodosLoading />
            <TodosLoading />
            <TodosLoading />
          </>
        )}
        onError={()=>(
          <TodosError />
        )}
        onEmpty={()=><EmptyTodos />}
        searchedTodos={searchedTodos}
        render={(todo) => (
          <TodoItem
            key={todo.text}
            text={todo.text}
            completed={todo.completed}
            onComplete={() => { completeTodo(todo.text) }}
            onDelete={() => { deleteTodo(todo.text) }}
          />
        )}
      />
      <TodoButton setOpenModal={setOpenModal} />

      {
        openModal && (
          <Modal>
            <TodoForm
              addTodo={addTodo}
              setOpenModal={setOpenModal}
            />
          </Modal>
        )
      }
    </>
  )
}



export default App

import { FC, ReactNode } from "react";
import { ITodo } from "../App/useTodos";
import './TodoList.css'

interface Props {
    loading: boolean,
    error: boolean,
    searchedTodos: ITodo[],
    onError: ()=>ReactNode,
    onLoading: ()=>ReactNode,
    onEmpty:()=>ReactNode,
    render: (value:ITodo)=>ReactNode,
}

const TodoList:FC<Props> = ({
    loading,
    error,
    searchedTodos,
    onLoading,
    onEmpty,
    onError,
    render
}) => {

    return (
        <ul className="TodoList">
            {loading && onLoading()}
            {error && onError()}
            {(!loading && searchedTodos.length === 0) && onEmpty()}
            { searchedTodos.map(render) }
        </ul>
    )
}

export { TodoList }
Muy buena clase, pude entender esto de las Render props 馃榿

Estoy perdidooo

No quise hacerlo igual que Juan por una sencilla raz贸n, y es que dejar铆amos atr谩s la composici贸n de componentes, yo propuse esta soluci贸n

<TodoList
          error={error}
          loading={loading}
          searchedTodos={searchedTodos}
          onError={() => <TodosError/>}
          onLoading={() => (
            new Array(5)
              .fill(1)
              .map((a, i) => <TodosLoading key={i}/>)
          )}
          onEmptyTodos={() => <EmptyTodos/>}
        >
            {searchedTodos.map(todo =>(
                <TodoItem 
                    key={todo.text} 
                    text={todo.text}
                    completed={todo.completed}
                    onComplete={() => toggleTodo(todo.text)}
                    onDelete={() => deleteTodo(todo.text)}
            />))}
        </TodoList>

El cambio es que no cree la propiedad render, me parece en mi opini贸n que es m谩s legible, espero sus opiniones