简体   繁体   English

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

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

I am trying to get all the states within the functional components using hooks.我正在尝试使用钩子获取功能组件中的所有状态。 Equivalent to ...this.state .相当于...this.state I am avoiding passing the state individually to the Context.Provider .我避免将状态单独传递给Context.Provider

Since this.state is not available within the function.由于this.state在函数中不可用。 state is undefined. 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 }

What's best way to pass all the states within the component to value in the provider.将组件内的所有状态传递给提供程序中的值的最佳方法是什么。

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

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>
  )
}

This can be further improved by using useReducer!这可以通过使用 useReducer 进一步改进! :) :)

You already have many states.你已经有很多状态了。 Don't use useState as you were using the setState function from classes.不要像使用类中的setState函数一样使用useState

An advice, If you don't wanna get confused and work with useState like you were using the setState from classes, use the same "labels" for the variable and try, if you can, to have one state.一个建议,如果您不想像使用类中的 setState 那样混淆并使用 useState,请为变量使用相同的“标签”,如果可以的话,尝试拥有一个状态。

// 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: '',
});

(Less code, easy to maintain) (代码少,易于维护)

About avoiding to passing the state through the Context Provider;关于避免通过Context Provider传递状态; it's not an option you have to .这不是你必须的选择。 Otherwise, there's no reason to use it.否则,没有理由使用它。

What I would do, it would be to keep the rest of your code and change the last lines of code.我会做的是保留其余代码并更改最后几行代码。 Having something like this:有这样的事情:

(btw, your fetchRecipe function is not receiving a parameter) (顺便说一句,您的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;

Of course this is just and example.当然,这只是一个例子。 You could also make use of useReducer like someone says.你也可以像有人说的那样使用useReducer This way you could treat your local state like you were working with Redux.这样你就可以像对待 Redux 一样对待你的本地状态。

Now you have two options depending if you are using an Stateful or Stateless component.现在您有两个选项,具体取决于您使用的是状态组件还是无状态组件。 For Stateful component: Get access to the context (value) of your provider using:对于有状态组件:使用以下方法访问提供程序的上下文(值):

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

// OR

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

For Stateless components:对于无状态组件:

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

What I explained above it could be found here: https://es.reactjs.org/docs/hooks-reference.html#usecontext .我上面解释的内容可以在这里找到: https : //es.reactjs.org/docs/hooks-reference.html#usecontext Also in the link, you will find an example of how to use useReducer .同样在链接中,您将找到有关如何使用useReducer That would be great in this case, instead of passing all the functions as I did, you could pass one single action dispatch and pass a type as the action you wanna trigger and get a new state from it.在这种情况下这会很棒,而不是像我那样传递所有函数,您可以传递一个单一的动作dispatch并传递一个类型作为您想要触发的动作并从中获取新状态。

But, you HAVE TO use the value from the context Provider .但是,你必须从上下文中使用的值Provider

You can use reducer this way, and add your context, you can follow this architecture example:您可以通过这种方式使用 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

One pattern I have used is to make individual state variables, then create a schema object that pulls them all together.我使用的一种模式是创建单独的状态变量,然后创建一个将它们全部拉在一起的模式对象。 This seems redundant, but it makes setting state simple (no crazy nested spread operators to set state) and then if you need to access your state variables by name somewhere else in your application, you can do that too.这似乎是多余的,但它使设置状态变得简单(没有疯狂的嵌套扩展运算符来设置状态),然后如果您需要按名称访问应用程序中其他位置的状态变量,您也可以这样做。 I do wish there was some easy way to automatically use a string to access the state variables, but I'm not aware of one.我确实希望有一些简单的方法可以自动使用字符串来访问状态变量,但我不知道有一种方法。

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')

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 如何正确地将数组从父 React 类组件状态传递给使用钩子编写的子组件? - How to properly pass an array from a parent React class component state to a child component written with hooks? 如何使用反应钩子将 state 从子组件传递给父组件? - How to use react hooks to pass a state from child component to the parent component? 如何从子组件反应钩子中仅更改 state 的一部分 - How to change only a part of the state from child component react hooks 使用钩子时如何重新渲染表单值和状态值,并且在不同的组件中更改状态 - How do I rerender form value and state value when using hooks and state is changed in a different component in react 在React中调用组件时如何传递状态(值)? - How Do I Pass State (Value) when Calling Component in React? 为什么这个 react Hooks 组件内的状态没有更新? - Why is the state not updating inside this react Hooks component? 使用 React Hooks,当我将 prop 从父组件传递给子组件时,子组件中的 prop 未定义 - Using React Hooks, when I pass down a prop from parent to a child component, the prop in the child component is undefined React Hooks:为什么将设置状态函数传递给子组件是不好的做法? - React Hooks : Why is it bad practice to pass the set state funcitons to a Child Component? 如果我有一个按钮在 React 的第二个子项中触发 state,如何将 state 传递给父组件 - How to pass a state to a Parent component if I have a button triggering the state in the 2nd Child in React 在 React 钩子中,如何将新创建的对象传递给状态对象? - In react hooks how do I pass a newly created object to a state object?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM