Как передать все переменные состояния внутри реагирующих хуков моему дочернему компоненту? - PullRequest
1 голос
/ 03 июля 2019

Я пытаюсь получить все состояния внутри функциональных компонентов с помощью хуков. Эквивалент ...this.state. Я избегаю передачи государства индивидуально на Context.Provider.

Поскольку this.state недоступно в функции. state не определено.

import React, { useState, useEffect } from 'react'

const RecipeContext = React.createContext()

const RecipeProvider = (props) => {
  const [showHomeButton, setShowHomeButton] = useState(false)
  const [recipes, setRecipes] = useState([])
  const [loading, setLoading] = useState(true)
  const [search, setSearch] = useState('')


  const fetchRecipe = async () => {
    const recipeData = await fetch(`https://api.myjson.com/bins/t7szj`)
    const { recipes } = await recipeData.json()
    setRecipes(recipes)
    setLoading(false)

  }
  const handleSubmit = async (e) => {
    e.preventDefault()
    setLoading(true)
    url = `${url}&q=${search}`
    fetchRecipe(url)
    setShowHomeButton(true)

  }
  const handleSearchChange = (e) => {
    setSearch(e.target.value)
  }
  const handleReturnHome = () => {
    fetchRecipe()
  }
  useEffect(() => {
    fetchRecipe()

  }, [])
  return (
    <RecipeContext.Provider value={}>
      {props.children}
    </RecipeContext.Provider>
  )
}
const RecipeConsumer = RecipeContext.Consumer
export { RecipeProvider, RecipeConsumer }

Какой лучший способ передать все состояния в компоненте в значение в поставщике.

 <RecipeContext.Provider value={}>
      {props.children}
    </RecipeContext.Provider>

Ответы [ 3 ]

1 голос
/ 03 июля 2019

Использование объекта в качестве состояния

const RecipeProvider = (props) => {
  //Declare an object as the state
  const [megaState, setMegaState] = useState({
      showHomeButton: false,
      recipes : [],
      loading : true,
      search: ''
  })



  const fetchRecipe = async () => {
    const recipeData = await fetch(`https://api.myjson.com/bins/t7szj`)
    const { recipes } = await recipeData.json()

    //UPDATE STATE WITHOUT MUTATING
    setMegaState({
        ...megaState
        recipes,
        loading: false
    })    
  }
  const handleSubmit = async (e) => {
    e.preventDefault()
    setLoading(true)
    url = `${url}&q=${search}`
    fetchRecipe(url)
    setShowHomeButton(true)
    //UPDATE STATE WITHOUT MUTATING
    setMegaState({
        ...megaState
        showHomeButton : true 
    })
  }
  const handleSearchChange = (e) => {
    //UPDATE STATE WITHOUT MUTATING
    setMegaState({
        ...megaState
        search : e.target.value 
    })
  }
  const handleReturnHome = () => {
    fetchRecipe()
  }
  useEffect(() => {
    fetchRecipe()

  }, [])
  return (
    <RecipeContext.Provider value={megaState}>
      {props.children}
    </RecipeContext.Provider>
  )
}

Это можно улучшить, используя useReducer!:)

0 голосов
/ 03 июля 2019

У вас уже много состояний.Не думайте о реакционном хуке, как вы привыкли думать, используя классы или функции, подобные предыдущим версиям реагирования.

Совет: если вы не хотите запутаться и работать с состояниями, как предложения, используйтете же самые «метки» для переменной и попытайтесь, если можете, иметь одно состояние

// From this
const [showHomeButton, setShowHomeButton] = useState(false);
const [recipes, setRecipes] = useState([]);
const [loading, setLoading] = useState(true);
const [search, setSearch] = useState('');

// to this - common understanding
const [state, setState] = useState({
  showHomeButton: false,
  recipes: [],
  loading: true,
  search: '',
});

(меньше кода, легко поддерживать)

Об избежаниипередача состояния через провайдера контекста, это не вариант, вам нужно .В противном случае, нет никакой причины использовать его.

Что бы я сделал, это было бы сохранить остальную часть вашего кода и немного изменить последние строки кода.Имея что-то вроде этого:

(кстати, ваша fetchRecipe функция не получает параметр)

import React, { useState, useEffect } from 'react'

const RecipeContext = React.createContext()

const RecipeProvider = (props) => {
  const [state, setState] = useState({
    showHomeButton: false,
    recipes: [],
    loading: true,
    search: '',
  });

  const fetchRecipe = async () => {
    const recipeData = await fetch(`https://api.myjson.com/bins/t7szj`);
    const { recipes } = await recipeData.json();
    setState({
      ...state,
      recipes,
      loading: false,
    });
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    fetchRecipe(`${url}&q=${search}`);

    setState({
      ...state,
      loading: true,
      showHomeButton: true
    });
  }

  const handleSearchChange = (e) => {
    e.persist();

    setState({
      ...state,
      search: e.target.value
    });
  };

  // this might not needed
  const handleReturnHome = () => {
    fetchRecipe()
  };

  useEffect(() => {
    fetchRecipe()

  }, []);

  return (
    <RecipeContext.Provider value={{
      store: state,
      actions: {
         fetchRecipe,
         handleSearchChange,
         handleSubmit,
      }
     }}>
      {props.children}
    </RecipeContext.Provider>
  )
}

export default RecipeProvider;

Конечно, это только пример.Вы также можете использовать useReducer, как кто-то говорит, так что вы можете обращаться с вашим локальным состоянием, как если бы вы использовали приставку.

Теперь у вас есть два варианта в зависимости от того, используете ли вы Statefull или компонент без состояния .

для statefull компонент: получить доступ к контексту (значению) вашего провайдера, используя:

<RecipeContext.Consumer>
  {value => (
   <SomeComponent />
  )}
</RecipeContext.Consumer>

// OR

class SomeComponent extends Component {
  render() {
   let value = this.context;
  }
}
SomeComponent. contextType = RecipeContext;

для Компоненты без состояния :

const SomeComponent = props => {
  const value = useContext(RecipeContext);
};

То, что я объяснил, можно найти здесь: https://es.reactjs.org/docs/hooks-reference.html#usecontext. Также в ссылке вы найдете пример использования useReducer,В этом случае было бы замечательно, вместо того, чтобы передавать все функции, как я, вы можете передать одно действие dispatch и передать тип как действие, которое вы хотите вызвать, и получить из него новое состояние..

Но вы ДОЛЖНЫ использовать значение из контекста Provider.

0 голосов
/ 03 июля 2019

Вы можете использовать Reducer таким образом, и добавить свой контекст, вы можете следовать этому примеру архитектуры:

const initState = {
  is_logged: false,
  token: "",
  error: { type: "", msg: "" },
  form: {
    first_name: "",
    last_name: "",
    password: "",
    email: ""
  }
}

const reducer = (state, action) => {
  const { payload } = action
  switch (action.type) {

    case "form_first_name":
      return { ...state, form: { ...state.form, first_name: payload } }
    case "form_last_name":
      return { ...state, form: { ...state.form, last_name: payload } }
    case "form_email":
      return { ...state, form: { ...state.form, email: payload } }
    case "form_password":
      return { ...state, form: { ...state.form, password: payload } }
    case "error":
      return { ...state, error: payload }
    case "success":
      return {
        ...state,
        token: payload,
        error: { type: "", msg: "" },
        is_logged: true
      }
    default:
      throw new Error()
  }
}

const AdminClinicContainer = () => {

  const [state, dispatch] = useReducer(reducer, initState)

  const _register = async () => {
    const result = await axios(API_ADMIN_REGISTER)
    console.log(result.data)
  }

  const _login = async () => {
    try {
      const response = await axios.post(API_ADMIN_LOGIN, {
        email: state.form.email,
        password: state.form.password
      })
      console.log(response.data)
      dispatch({ type: "success", payload: response.data.token })
    } catch (error) {
      console.log(error.response.data.error)
      dispatch({ type: "error", payload: error.response.data.error })
    }
  }

  const _forgetPsw = async () => {
    const result = await axios(API_ADMIN_LOGIN)
    console.log(result.data)
  }
  const _form = (type, payload) => dispatch({ type, payload })

  return (
    <div>
      <AdminClinic
        _register={_register}
        _login={_login}
        _forgetPsw={_forgetPsw}
        _form={_form}
        state={state}
      />
    </div>
  )
}

export default AdminClinicContainer
...