简体   繁体   中英

useState won't update the state when I set it in react

I need to render a component that has a route using react router. the first component has a button that when clicked needs to render another component that has state passed in from the first component. The page redirects but doesn't load. All of the data from the first component I want is passed in but it wont set state when I use setProfile(p). All the other console.log()s in the member component show all the data I expect but it won't set the state with this data.

import {useLocation} from "react-router-dom";
    const Member = (props)=> {  
      const [user, setUser] = useState({});
      const [profile, setProfile] = useState({});

  const [user, setUser] = useState({});
  const { state } = useLocation();
  const [profile, setProfile] = useState({});
  const dispatch = useDispatch();
  const [list, setList] = useState([]);
  const [posts, setPosts] = useState([]);
  const [snInstance, setsnInstance] = useState({});
    
   // run effect when user state updates
    useEffect(() => {
  const doEffects = async () => {
    try {
    //  const p = await incidentsInstance.usersProfile(state.user, { from: accounts[0] });
    //  const a = await snInstance.getUsersPosts(state.user, { from: accounts[0] });

      if (state && state.user) {
        setUser(state.user);
      }

      const accounts = await MyWeb3.getInstance().getAccounts();
      setAccounts(accounts);
      console.log(accounts)
      const incidents = MyWeb3.getInstance().getContract(Incidents)
      const incidentsInstance = await MyWeb3.getInstance().deployContract(incidents);
      const sn = MyWeb3.getInstance().getContract(SocialNet)
      const snInstance = await MyWeb3.getInstance().deployContract(sn);
      setsnInstance(snInstance);
      const pro = socialNetworkContract.members[0]
      console.log(pro)
      const p = await incidentsInstance.usersProfile(pro, { from: accounts[0] });
      const a = await snInstance.getUsersPosts(pro, { from: accounts[0] });
      console.log(a)
      console.log(p)
      setProfile(p)

        } catch (e) {
      console.error(e)
    }
  }
    doEffects();
  }, [profile, state]);

  const socialNetworkContract = useSelector((state) => state.socialNetworkContract)

  return (
    <div class="container">

                <a target="_blank">Name : {profile.name}</a>

        {socialNetworkContract.posts.map((p, index) => {
          return <tr key={index}>
{p.message}
        </tr>})}
      </div>
  )
}

export default Member;

This is the parent component I want to redirect from

   const getProfile = async (member) => {
        const addr = dispatch({ type: 'ADD_MEMBER', response: member })
        console.log(member)
    }


  const socialNetworkContract = useSelector((state) => state.socialNetworkContract)

    
      return (
          <div>
            {socialNetworkContract.posts.map((p, index) => {
              return <tr key={index}>
    
      <button onClick={() => getProfile(p.publisher)}>Profile</button>
    
            </tr>})}
          </div>
      )
    }
export default withRouter(Posts);

I have this component working when I don't have a dynamic route that needs data passing in from the parent component It's redirecting from.

My routes.js looks like

const Routes = (props) => {
  return (
      <Switch>

      <Route path="/member" exact component={Member} /> 
        <Route path="/posts" exact component={Posts} />
        <Redirect exact to="/" />
      </Switch>
    
  )
}

export default Routes

This is the reducer

import { connect, useDispatch, useSelector } from "react-redux";
let init = {
    posts:[],
    post:{},
    profiles:[],
    profile:{},
    members:[],
    member:{}
}
export const socialNetworkContract = (state = init, action) => {
    const { type, response } = action;
    switch (type) {
        case 'ADD_POST':
            return {
                ...state,
                posts: [...state.posts, response]
            }
        case 'SET_POST':
            return {
                ...state,
                post: response
            }
        case 'ADD_PROFILE':
            return {
                ...state,
                profiles: [...state.profiles, response]
            }
        case 'SET_PROFILE':
            return {
                ...state,
                profile: response
            }
        case 'ADD_MEMBER':
            return {
                ...state,
                members: [...state.members, response]
            }
        case 'SET_MEMBER':
            return {
                ...state,
                member: response
            }

        default: return state
    }
};

It doesn't make any sense that you would dispatch({ type: 'ADD_MEMBER', response: member }) with a member object that came from the publisher property of a post . That info is already in your state. You probably need to be normalizing your state better so that you can select it where you need it.


You want to use the Link component from react-router-dom to navigate to a member's profile page. Your Route should render the correct profile based on an id or username property in the URL. Don't pass through the data when you redirect, just go to the correct URL. On that Member page you can get the user from the state by looking up the id .

In Posts :

<Link to={`/member/${p.publisher.id}`}><button>Profile</button></Link>

In Routes :

<Route path="/member/:id" component={Member} />

In Member :

const Member = () => {
  const { id } = useParams();

  const profile = useSelector((state) =>
    state.socialNetworkContract.members.find((user) => user.id === id)
  );

  const dispatch = useDispatch();

  useEffect(() => {
    const doEffects = async () => {
      if ( ! profile ) {
        dispatch(loadUser(id));
      }
    };
    doEffects();
  }, [dispatch, profile, id]);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM