如何将反应钩子内的所有状态变量传递给我的子组件

How do I pass all state variables inside react hooks to my child component

我正在尝试使用挂钩获取功能组件中的所有状态。相当于...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>

你可以这样使用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

Use an object as the state

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 进一步改进! :)

您已经有很多状态。不要像使用 类.

中的 setState 函数那样使用 useState

一个建议,如果您不想像使用 类 中的 setState 一样使用 useState,请对变量使用相同的 "labels" 并尝试,如果可以的话, 有一个状态。

// 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 。这样你就可以像对待 Redux 一样对待你的本地状态。

现在您有两个选项,具体取决于您使用的是 Stateful 还是 Stateless 组件。对于 Stateful 组件:使用以下方式访问提供者的上下文(值):

<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。 同样在 link 中,您将找到如何使用 useReducer 的示例。这在这种情况下会很棒,而不是像我那样传递所有函数,你可以传递一个动作 dispatch 并传递一个 type 作为你想要触发的动作和从中获取新状态。

但是,您必须使用上下文中的值Provider

我使用的一种模式是制作单独的状态变量,然后创建一个将它们拉到一起的模式对象。这似乎是多余的,但它使设置状态变得简单(没有疯狂的嵌套展开运算符来设置状态),然后如果您需要在应用程序的其他地方通过名称访问状态变量,您也可以这样做。我真希望有一些简单的方法可以自动使用字符串访问状态变量,但我不知道有一个。

const [name, setName] = useState('')
const [email, setEmail] = useState('')

// for easy access to all state variables in child components
// using a regular definition within the component means you're
// always referring to the latest version of the state variables
// imagine if you had like 20 state variables to pass...
const schema = {
  name: {
    state: name,
    setState: setName,
  },
  email: {
    state: email,
    setState: setEmail,
  },
}

// elsewhere, maybe in a child component
schema['name'].setState('Steve')